Git Product home page Git Product logo

til's Introduction

Today I Learned

知ったこと考えたことをIssueに記録していきます。

Issueのルール

  • 1トピック1Issue
  • [2019/01/01] [タイトル]の形式
  • Labelはなるべく付ける(複数も可)
  • Issueはクローズにしない

til's People

Contributors

manabuyasuda avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

til's Issues

2019/09/06 省略機能を付与するClamp.js

背景がグラデーションで、Sassのmixinでは対応できないものがあった。もちろんIE11対応あり。

いちばんスターが付いていて、「css-tricks」でも紹介されているClamp.jsを採用した。

結果としては採用したが、IE11(parallels desktop)でclamp: 3としても2行になってしまったりした(日本語でも英語でも)。CodePenを見ると動いてそうなので、jQuery必須って感じなのか。
2013年から更新が止まっていて、Issueやプルリクエストも溜まっている状態を見ると、やや不安なライブラリかもしれない。

ちなみに「ftellipsis」はIEでうまく動かなかった。

インストール

npm i clamp-js

JS

. ellipsisに対して処理が実行される。
-webkit-line-clampに対応のブラウザでは-webkit-line-clampを、非対応のブラウザではテキスト自体が切り取られてが追加される。
リサイズにも対応していて、元のテキストを保存しているようだ。

import $clamp from 'clamp-js';

const els = document.getElementsByClassName('ellipsis');

Array.from(els).forEach(el => {
  $clamp(el, {clamp: 3});
});

2019/07/17 メディアクエリを監視するヘルパー関数

window.matchMedia()はメディアクエリを監視するのに便利。
よく使うだけに関数化しておきたいと思っていた。
メディアクエリの条件をデータとしてどこかに定数化もしておきたかった。

yuheiyさんのreal-world-website-boilerplateを丸パクリしてみた。

const BROWSER_DEFAULT_FONT_SIZE = 16;

export const MEDIA_QUERY_SMALL = `(min-width: ${375 / BROWSER_DEFAULT_FONT_SIZE}em)`;
export const MEDIA_QUERY_MEDIUM = `(min-width: ${768 / BROWSER_DEFAULT_FONT_SIZE}em)`;
export const MEDIA_QUERY_LARGE = `(min-width: ${1024 / BROWSER_DEFAULT_FONT_SIZE}em)`;
export const MEDIA_QUERY_XLARGE = `(min-width: ${1440 / BROWSER_DEFAULT_FONT_SIZE}em)`;

/**
 * @desc メディアクエリがロードで判定またはリサイズで判定が切り替わったときに、コールバックで真偽値を返します。
 * @link https://github.com/yuheiy/real-world-website-boilerplate/blob/master/src/js/mediaQuery.js
 * @param {string} mediaQuery - メディアクエリ文字列
 * @param {boolean} layoutChangedCallback - メディアクエリ文字列が一致するかの真偽値
 * @example
 * import { installMediaQueryWatcher, MEDIA_QUERY_MEDIUM } from './mediaQuery';
 * installMediaQueryWatcher(MEDIA_QUERY_MEDIUM, matches => {
 *   console.log(matches ? 'MEDIA_QUERY_MEDIUM' : 'MEDIA_QUERY_SMALL');
 * });
 */
export const installMediaQueryWatcher = (mediaQuery, layoutChangedCallback) => {
  const mql = window.matchMedia(mediaQuery);
  const listener = event => {
    layoutChangedCallback(event.matches);
  };
  mql.addListener(listener);
  layoutChangedCallback(mql.matches);
  const uninstall = () => {
    mql.removeListener(listener);
  };
  return uninstall;
};

理想的には、エンティティやユースケースのようなディレクトリで分割したかったが、そこまで厳密に設計する案件がほとんどないので、エントリーポイントと同階層にそういった関数を置いていくことにした。

汎用系関数はutil/foo.jsとしてるが、utility.jsとしてみてもいいかもしれない。

2019/11/07 IEでSVGタグにフォーカスが当たらないようにする`focusable`属性

button > svgのような構造でフォーカスを移動させると、IEではSVGにもフォーカスされてしまう。
tabindex="-1"を指定しても効果がなかった。

調べたところ、IEだけfocusableに対応していて、それがtabindexよりも優先されてしまうらしかった。

対応としては、<svg focusable="false"></svg>と指定する。

Twitterで@_yuheiyさんに一覧できるページを教えてもらった。
https://allyjs.io/data-tables/focusable.html

2019/07/26 Vueコンポーネントの分類とVuexの責務

名前 責務 State props event Atomic Design
Container Component その画面の構成を表現し、それ以外の画面との関係性を表現する。 × × Pages
Factory Component コンポーネントを動的に切り替えるラッパーコンポーネント。:isで指定して、propsでデータを渡す。 × × Templates
ViewModel Component ある範囲でのプレゼンテーション(View)とロジック(ViewModel)を持ったコンポーネント。単体でも動くが、View Componentに分割してもよい。 × Organisms
View Component 親コンポーネントから受け取ったpropsだけで動作するコンポーネント。親コンポーネントに依存している。繰り返し処理をしたり、イベントに応じて処理が発生する。 × Molecules
Design Component デザインの共通化を目的としたコンポーネント。HTMLタグのような役割を持っている。実装はView Componentと同じ。 × Atoms
名前 責務
store ルートコンポーネントにVue.use(Vuex)でVuex Storeを注入できる。computedでthis.$store.state.xxxとすることでViewにリアクティブに反映される。
getter Vuex Storeから受け取ったデータを加工するのはViewModelの役割。Getterは複数のViewModelで重複するようなVuex Storeの加工処理を受け持つ。細かな処理はViewModelに任せる。
Mutations Vuex Storeを更新する唯一の方法。コンポーネントでthis.$store.commit(type, payload)を実行すると、typeで渡されたmutationが実行される。payloadでmutationに値を渡すこともできる(オブジェクトを推奨)。非同期処理はActionsでcommitする。
Actoins "mutationを非同期でcommitしたり、複数のmutationをまとめてcommitできる。コンポーネントでthis.$store.dispatch('xxx')を実行するとmutationが実行される。

2019/08/08 CSS Gridで均等なグリッドレイアウトをmixinで

CSS Gridに入門した。
カラムを定義するgrid-template-columnsと、上下左右のギャップ(ガター)を定義するrow-gapcolumn-gapが便利だ。
CSS Gridっぽく使うならgrid-columngrid-rowだが、mixinで定義しておくものでもなさそうなので、よく使うグリッドシステムをCSS Gridで作ってみた。

ベーススタイル

ブレイクポイントの変数

$mq-breakpoints: (
  sm: 375px,
  md: 768px,
  lg: 1024px,
  xl: 1440px,
);

column-gapはデフォルトで指定する。
row-gapは必要な時に指定する。

.Grid {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: 15px;

  @include mq(lg) {
    column-gap: 30px;
  }
}

.Grid.\-rowGap {
  row-gap: 20px;

  @include mq(lg) {
    row-gap: 40px;
  }
}

均等なグリッド

-1/1 -1/2@md -1/3@lgのようにカラム数を指定する。

.Grid(class="-1/1 -1/2@md -1/3@lg -rowGap")
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
@mixin equal-column($key: null) {
  $columns: $flex-equal-column;
  $suffix: "";

  @if $key != null {
    $suffix: \@#{$key};
  }

  @each $column in $columns {
    .Grid.\-1\/#{$column}#{$suffix} {
    grid-template-columns: repeat(#{$column}, 1fr);
    }
  }
}

/**
 * 1/2と1/3のような均等なカラムを生成します。
 */
@include equal-column();

@each $name, $breakpoint in $mq-breakpoints {
  @media (min-width: em($breakpoint)) {
    @include equal-column($name);
  }
}

生成されたCSS。Autoprefixerでベンダープレフィックスを付ける想定。

.Grid.\-1\/1 {
  -ms-grid-columns: (1fr)[1];
  grid-template-columns: repeat(1,1fr)
}
.Grid.\-1\/2 {
  -ms-grid-columns: (1fr)[2];
  grid-template-columns: repeat(2,1fr)
}

8/12と4/12のようなグリッド

.Grid(class="-8to4@md")
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
  .Grid_Item Grid_Item
@mixin column($key: null) {
  $columns: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 !default;
  $suffix: "";

  @if $key != null {
    $suffix: \@#{$key};
  }

  @each $column in $columns {
    $lastColumn: 12 - $column;
    .Grid.\-#{$column}to#{$lastColumn}#{$suffix} {
      grid-template-columns: ($column * 1fr) ((12 - $column) * 1fr);
    }
  }
}

/**
 * 6/12と6/12のようなカラムを生成します。
 */
@include column();

@each $name, $breakpoint in $mq-breakpoints {
  @media (min-width: em($breakpoint)) {
    @include column($name);
  }
}

生成されたCSS。

.Grid.\-1to11 {
  -ms-grid-columns: 1fr 11fr;
  grid-template-columns: 1fr 11fr
}
.Grid.\-2to10 {
  -ms-grid-columns: 2fr 10fr;
  grid-template-columns: 2fr 10fr
}

2019/07/23 Sassの&を使った結合セレクターを禁止する

以前「Sassの使い方 – 誰にとっても使いやすいCSSにするための工夫」にも書いたが、Sassの&は使い方を制限したほうがいい。

使ってもいいのは擬似要素と擬似クラス、.is-*のようなステートだ。参照する&は常に同じキーセレクター(最初のブレースにあるセレクター)であることが望ましい。

禁止するのは&__elementのようにセレクターを結合して作ってしまうパターンだ。
結合セレクターを使い出すと、縦に長くなって読みにくくなり、入れ子が深くなるごとに&の中身が変わっていくことが多い。
たとえばJavaScriptで変数の中身が何度も変わってしまうと、コードを追うのが難しくなり、バグが起きやすくなる。これのSass版だと思えばいい。

検索しにくいというデメリットもある。
ブラウザで確認してblock--modifierの修正が必要だとわかった。検索しても出てこない、&--modifierになっていたからだ。
書き方としてはDRYかもしれないが、読みやすさと検索しやすさはマイナスになる。blockの修正を1箇所にできるというDRYは、エディターの機能で十分代用できるので、本当に必要なのか考えてみるといい。
個人的に&を使ったネストの深いSassはまったく読めないので使わない方針だ。

stylelint-scss 3.9.0で結合セレクターを禁止するオプションselector-no-union-class-nameが追加されている。

&-union&_union&unionのようなセレクターを許可しなくなる。
&.foo& pのような結合していないセレクターは警告されない。

// stylelintrc
"scss/selector-no-union-class-name": true

参照:

2019/10/30 Dockerでファイル変更が効かなくなったら

Docker for Macはdocker imageを管理するためにDisk imageを持っている。Disk imageはイメージをビルドするたびに少しずつ溜まっていくらしい。

prederenceのDiscタブでDisk imageのサイズが確認できる。
https://gyazo.com/7b1920d89ad1c9604eb0ae863b6ef4fe

Resetタブの「Reset disc image」をクリックすると、すべてのDocker imageとコンテナを削除できる。設定は変わらない。
https://gyazo.com/c618366bbd553d9d1b7030cffed74de3

無事削除できた。
https://gyazo.com/84f06dd0543c3f5996bce79f11ac1349

Disk imageを削除したらsh start.shからやり直す必要がある。

参考記事
https://blog.pinkumohikan.com/entry/cleanup-docker-disk-image

2019/07/09 Tree ShakingでJS(webpack、Babel、lodash)の最適化をした

manabuyasuda/website-template#253

「Tree Shaking」とは、webpackなどのバンドルツールが、利用されていないコード(デッドコード)を出力に含めないようにする機能のこと。

Tree Shakingの条件

  • importexport構文を使っている(require()は対応していない)
  • productionモードで実行している
  • Babelの設定で"modules": falseにしている(importexport構文をrequireに変換しない)

.babelrcは以下の設定にした。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

lodashをTree Shakingする

lodashはES modules(importexport構文)に対応していない。
lodash-esを使うことでTree Shakingの対象に含めることができる。

npm i lodash-es

lodash自体をインポートすると97KBだったのに対して、

import _ from 'lodash-es';
_.map();

必要なモジュールのみインポートした場合は17KBと82.5%の削減ができた。

import { map } from 'lodash-es';
map();

ライブラリを使う場合は、それがES modulesに対応しているかを確認しておくのがよさそう。とくにjQuery時代から使っているものは。

参照:

2019/07/30 vuejs-paginateとHistory APIでページネーションを作る

Vue.jsのページネーションを検索して出てきたvuejs-paginateを実装してみた。

要件

  • Paginate.vueにpropsで一覧のデータを渡す想定
  • PaginateList.vueでページネーションを出力、パラメーターを付与してブラウザバックに対応する
  • パラメーターの取得にgetExceptParameter.jsとgetParameter.jsを使っている

2019/07/23 Vue.jsのSFCでタグ名をpropsで変更する

BaseButton.vueコンポーネントを作ったとする。
タグはaの時もbuttonの時もある。
VuetifyなどではcreateElementを使ってタグ名を変更できるようにしていた。

とはいえ、templateの方が見通しがいいので、createElementを使わない方法を探していたら:isを使ったサンプルがあった。

HTML側はpropstagにタグ名を渡すと想定する。

sample-button(tag="button" type="button") Button
sample-button(tag="a" href="#") Button

SFC側はpropsとしてtagを設定する。

  props: {
    tag: {
      type: String,
      requires: false,
      default: 'a',
      validator(val) {
        return ['a', 'button'].includes(val);
      },
    },

template:isthis.tagでpropsを渡す。

<template>
  <a
    :is="this.tag"

以下のように出力される。

<button data-v-4dee8d32 type="button">Button</button>
<a data-v-4dee8d32 href="#">Button</a>

inputabuttonと違って閉じタグがないので、パターンには入れないか、v-ifで1つだけ分岐を作って出し分ける。

2019/09/26 便利な`console`メソッド

Moving beyond console.log() — 8 Console Methods You Should Use When Debugging JavaScript and Node

  • falseならログを出力するconsole.assert()
  • 文字列の記録回数をカウントするconsole.count()console.countReset()
  • コンソールをまとめて表示するconsole.group()console.groupEnd()
  • データをテーブル表示するconsole.table()
  • パフォーマンスを計測するconsole.time()console.timeEnd()

console.assert()

console.assert()は第一引数に真偽値を渡す。trueの場合はログを実行せず、falseの場合に第二引数以降をログに残す。

const isTrue = true;
const isFalse = false;

console.assert(isTrue, 'これは表示されません。');
console.assert(isFalse, { isFalse }); // Assertion failed: {isFalse: false}

console.count()console.countReset()

console.count()は特定の文字列がコンソールに記録された回数をカウントする。
console.countReset()でカウントをリセットする。

const hello = 'Hello';

console.count(hello); // Hello: 1
console.count(hello); // Hello: 2
console.count(hello); // Hello: 3
console.countReset(hello);
console.count(hello); // Hello: 1

console.group()console.groupEnd()

console.group()でグループのラベルを渡す。
console.groupEnd()でそのグループの出力を終了する。

console.group('A to B');
console.log('A');
console.log('B');
console.groupEnd();

Screenshot from Gyazo

console.table()

console.table()はオブジェクトや配列をテーブル表示する。ソート機能もあり、複雑なデータの把握ができる。

const user = [{ name: 'name1', age: 1 }, { name: 'name2', age: 2 }];

console.table(user);

Screenshot from Gyazo

console.time()console.timeEnd()

console.time()で開始、console.timeEnd()でパフォーマンスを計測する。
Date.now()よりも正確で細かい結果を返す。

Date.now()はms単位までしか計測できない。

const start = Date.now();
for (let i = 0; i < 1000000; i++) {
  i + i;
}
const diff = Date.now() - start;
console.log(`Time execution with Date.now: ${diff} ms`); // Time execution with Date.now: 3 ms

console.time()console.timeEnd()は小数点12桁まで計測できる。

console.time('Time execution with console.time');
for (let i = 0; i < 1000000; i++) {
  i + i;
}
console.timeEnd('Time execution with console.time'); // Time execution with console.time: 3.454833984375ms

2019/07/08 cross-envとdotenvを導入した

環境変数をNuxt.jsやLaravel Mixでも使われているcross-envを使用することにした。
上記に加えて、共有しない環境変数をdotenvで管理することにした。

cross-envの利用状況としては、

  • 開発と公開の処理切り替え(webpackのmodeや、ミニファイの有無など)
  • 開発と公開のデータ切り替え(APIの参照先を変えたりとか)

dotenvの利用状況としては、

  • browser-syncのhttpsオプションに証明書のパスを指定するかしないか

環境変数を渡す方法として、webpackではwebpack.DefinePlugin、Nuxt.jsではenvプロパティがある。

gulpfile.js内でも使えるので、PugなどのJS以外にも利用できる。

2019/08/26 都道府県のデータを一元管理する

コンテンツに都道府県を含むサイトの場合、URLに都道府県名のローマ字やエリア判定で地方名などを変数化して管理する。
変数化しない場合は、修正対応が煩雑になり、JSのデータとロジックが散らばり、ケアレスミスが増えやすくなる。

変数化は変数に都道府県ごとのオブジェクトを設定するのがよさそう。
都道府県コードはidに、英語読み(ローマ字)はenに、地方分けはregionとした。
https://gist.github.com/manabuyasuda/c24b832e11db33625637987815c88964

const PREFECTURES = [
  { id: '1', name: '北海道', en: 'hokkaido', region: '北海道' },
  { id: '2', name: '青森県', en: 'aomori', region: '東北' },
  { id: '3', name: '岩手県', en: 'iwate', region: '東北' },
  { id: '4', name: '宮城県', en: 'miyagi', region: '東北' },
  { id: '5', name: '秋田県', en: 'akita', region: '東北' },
  { id: '6', name: '山形県', en: 'yamagata', region: '東北' },
  { id: '7', name: '福島県', en: 'fukushima', region: '東北' },
  { id: '8', name: '茨城県', en: 'ibaraki', region: '関東' },
  { id: '9', name: '栃木県', en: 'tochigi', region: '関東' },
  { id: '10', name: '群馬県', en: 'gunma', region: '関東' },
  { id: '11', name: '埼玉県', en: 'saitama', region: '関東' },
  { id: '12', name: '千葉県', en: 'chiba', region: '関東' },
  { id: '13', name: '東京都', en: 'tokyo', region: '関東' },
  { id: '14', name: '神奈川県', en: 'kanagawa', region: '関東' },
  { id: '15', name: '新潟県', en: 'niigata', region: '中部' },
  { id: '16', name: '富山県', en: 'toyama', region: '中部' },
  { id: '17', name: '石川県', en: 'ishikawa', region: '中部' },
  { id: '18', name: '福井県', en: 'fukui', region: '中部' },
  { id: '19', name: '山梨県', en: 'yamanashi', region: '中部' },
  { id: '20', name: '長野県', en: 'nagano', region: '中部' },
  { id: '21', name: '岐阜県', en: 'gifu', region: '中部' },
  { id: '22', name: '静岡県', en: 'shizuoka', region: '中部' },
  { id: '23', name: '愛知県', en: 'aichi', region: '中部' },
  { id: '24', name: '三重県', en: 'mie', region: '近畿' },
  { id: '25', name: '滋賀県', en: 'shiga', region: '近畿' },
  { id: '26', name: '京都府', en: 'kyoto', region: '近畿' },
  { id: '27', name: '大阪府', en: 'osaka', region: '近畿' },
  { id: '28', name: '兵庫県', en: 'hyogo', region: '近畿' },
  { id: '29', name: '奈良県', en: 'nara', region: '近畿' },
  { id: '30', name: '和歌山県', en: 'wakayama', region: '近畿' },
  { id: '31', name: '鳥取県', en: 'tottori', region: '**' },
  { id: '32', name: '島根県', en: 'shimane', region: '**' },
  { id: '33', name: '岡山県', en: 'okayama', region: '**' },
  { id: '34', name: '広島県', en: 'hiroshima', region: '**' },
  { id: '35', name: '山口県', en: 'yamaguchi', region: '**' },
  { id: '36', name: '徳島県', en: 'tokushima', region: '四国' },
  { id: '37', name: '香川県', en: 'kagawa', region: '四国' },
  { id: '38', name: '愛媛県', en: 'ehime', region: '四国' },
  { id: '39', name: '高知県', en: 'kochi', region: '四国' },
  { id: '40', name: '福岡県', en: 'fukuoka', region: '九州' },
  { id: '41', name: '佐賀県', en: 'saga', region: '九州' },
  { id: '42', name: '長崎県', en: 'nagasaki', region: '九州' },
  { id: '43', name: '熊本県', en: 'kumamoto', region: '九州' },
  { id: '44', name: '大分県', en: 'oita', region: '九州' },
  { id: '45', name: '宮崎県', en: 'miyazaki', region: '九州' },
  { id: '46', name: '鹿児島県', en: 'kagoshima', region: '九州' },
  { id: '47', name: '沖縄県', en: 'okinawa', region: '九州' },
];

ユースケースとして、2つの関数を用意した。
「都」「府」「県」を除去するnormalize()と、任意の地方のデータを抜き出すgetRegion()

/**
* 「都」「府」「県」を除去します。
* @param {string} name 都道府県名
* @return {string} 都府県を除去した文字列
* @example
* normalize(PREFECTURES[0].name); // 北海道
* normalize(PREFECTURES[1].name); // 青森
* normalize(PREFECTURES[12].name); // 東京
* normalize(PREFECTURES[26].name); // 大阪
* PREFECTURES[26].name; // 大阪府
*/
const normalize = name => name.replace(/都|府|県/g, '');

/**
* 指定した値と同じ地方のデータを取得します。
* @param {string} name
* @return {array} 一致した都道府県の配列
* @example
* getRegion('北海道'); // [0: {id: "1", name: "北海道", en: "hokkaido", region: "北海道"}]
*/
const getRegion = name => {
 return PREFECTURES.reduce((result, current) => {
   if (name === current.region) {
     result.push(current);
     return result;
   }
   return result;
 }, []);
};

2019/07/24 CSSカスタムプロパティを使っているフレームワークでIE対応をする

Material Components for the webのようにCSSカスタムプロパティを使用しているフレームワークがある。
IEはCSSカスタムプロパティ非対応なので、現状では対策が必要。

  1. ponyfillのcss-vars-ponyfillを使う
  2. postcss-custom-propertiesで静的CSSに変換する

ponyfillのcss-vars-ponyfillを使う

:root以外でCSSカスタムプロパティを使っていない場合に使える。JSで動的に変換できる点で優位。

ドキュメントにあるようにインストールする。

npm install css-vars-ponyfill

インポートして実行する。

import cssVars from 'css-vars-ponyfill';
cssVars();

headタグ内にテスト用の変数を追加する。

  <style>
    :root {
      --color: black;
    }
  </style>
</head>

body内にdivを追加して反映を確認する。

<body>
  <div>div</div>

CSSを追加する。:root以外での反映を確認するため、div--color: red;を追加した。

:root {
  --a: var(--b); /* Chained */
  --b: var(--c);
  --c: 10px;
}

div {
  --color: red;

  color: var(--color); /* from <style> */
  margin: var(--unknown, 20px); /* Fallback */
  padding: calc(2 * var(--a)); /* Nested */
}

IEのみheadタグ内に以下のようにstyleタグでインライン出力された。

<style data-cssvars="out" data-cssvars-job="1" data-cssvars-group="1">
</style>
  div {
    color: black;
    margin: 20px;
    padding: calc(2 * 10px);
  }

div {}内の--color: red;は変換されなかった。

postcss-custom-propertiesで静的CSSに変換する

Laravel Mixだと以下のように設定する。preserve: falseにすると、var()は削除される。
JSで動的に追加されたカスタムプロパティは非対応ブラウザで反映されない。

mix
  .js('src/assets/js/app.js', 'dist/assets/js/')
  .sass('src/assets/css/app.scss', 'dist/assets/css/')
  .options({
    processCssUrls: false,
    autoprefixer: {
      options: {
        grid: true,
      },
    },
    postCss: [
      require('postcss-custom-properties')({
        preserve: false
      })
    ],
  });

2019/08/07 言語に応じた日付と時刻にするIntl.DateTimeFormat

Intl.DateTimeFormat

const date = new Date(2012, 1, 1, 13, 1, 0);

console.log(
  new Intl.DateTimeFormat('ja', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
    hour12: true,
  }).format(date),
); // => 2012年2月1日水曜日 午後1:01

console.log(
  new Intl.DateTimeFormat('ja', {
    weekday: 'short',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).format(date),
); // => 2012/02/01(水) 13:01

完全にフォーマットしきれないところは、replace()とかで調整が必要そう。

2019/07/25 gulp-iconfontでビルドごとに差分が出る問題

gulp-iconfontを使うと、変更がなくてもビルドごとに差分が出てしまう。
内包しているgulp-svg2ttfのoptions.timestampによるもの。デフォルトがMath.round(Date.now()/1000)のため、何もしなければ差分が発生する。

Issueにも上がっていた。
要約すると、「gulp-svg2ttfのtimestamp optionを見てね」「0に設定してもいいけど、Gulpを起動した時間に設定するのが最善だよ」「gulpfile.js内でtimestampがあるかの条件分岐を作る方法があるよ」。

2019/09/05 ボトムアップドメイン駆動設計を読んだ

フロントエンドでDDD(OOP)を取り入れられないかなーと思って調べてて、このサイトが優しく雰囲気を教えてくれてよかった。
https://nrslib.com/category/programming/ddd/

  • 値オブジェクト:データを扱うルールを名詞ごとにまとめたもの。不変。値オブジェクトは別の値オブジェクトを使うこともできる。例 UserUserNamePhoneNumber
  • エンティティ:値オブジェクト以外の最小要素。可変で状態を持つ。識別子によって同一と判定する。 例 UserArticle
  • ドメインサービス:値オブジェクトやエンティティが持つと不自然になるメソッドを持つ。例 User エンティティ同士を比較するメソッドを UserService が持つ。
  • リポジトリ:データの永続化の処理をする。エンティティ�(集約)ごとに作成する。エンティティから永続化の方法を切り離す(エンティティのテストをしやすくする)。
  • 集約:あるエンティティを操作するエンティティ。集約は別の集約を使うこともできる。
  • アプリケーションサービス:ドメイン領域で何ができるのかを表現する(UseCaseと同じ?)。

これにプラスして、関数型プログラミング(FP)の考え方を取り入れていくといいのかな。
FPとOOPはバッティングするという意見もあるようですが。

2019/07/31 History APIでブラウザ履歴を変更する

テスト段階だが、History APIを使った処理をclassとしてまとめた。

History.js

  • init()updateParameterが実行されパラメーターを取得する
  • margeParameter(parameter)でパラメーター(Object)を結合する
  • registerPushState()で履歴を追加する

2019/08/08 動的なclassと静的なclass

class構文はインスタンス化が前提ではないと気づいた。
staticキーワードで静的メソッドにすると、インスタンス変数を変更されてしまうのを避けることができる。
インスタンス化する場合はTabとかAccordionのようなUI処理をするとき、静的にするのは値を不変にして内部処理で完結できる場合。

インスタンス化する動的なclass

インスタンス化する場合は、初期化時にインスタンス変数を変更できることがメリット。対象の要素を変更するといった場合に使える。Tab.jsみたいな。
その反面、variableClass1.width = 100;のようにアクセスして上書きすることができてしまう。「プライベートフィールド宣言」という仕様もあるが、Babelを設定しても使えなかった。

export default class VariableClass {
  constructor(options) {
    const defaultOptions = {
      width: 0,
      height: 0,
    };

    this.options = Object.assign(defaultOptions, options);

    Object.keys(this.options).forEach(key => {
      this[key] = this.options[key];
    });
    console.log(this.options); // => { width: 0, height: 0 }
    console.log(this.width); // => 0
  }
}

const variableClass1 = new VariableClass();

const variableClass2 = new VariableClass({
  width: 100,
});

console.log(variableClass1.width); // => 0
console.log(variableClass2.width); // -> 100

variableClass1.width = 100;
console.log(variableClass1.width); // => 100

staticメソッドで動的なclass

getterを設定することで変数を取得することができる。設定しなければプライベート変数になるのでアクセスできない。
classブロックの中はメソッドしか設定できないので、constなどを入れることはできない。

const width = 0;

export default class StaticClass {
  static get width() {
    return width;
  }
}
import StaticClass from './StaticClass';

console.log(StaticClass.width); // => 0

setterも設定すると変数を上書きすることもできる。

let width = 0;

export default class StaticClass {
  static get width() {
    return width;
  }

  static set width(prop) {
    width = prop;
  }
}
StaticClass.width = 100;
console.log(StaticClass.width); // => 100

setterを設定せずにStaticClass.width = 100;とした場合は、Uncaught TypeError: Cannot set property width of function StaticClass()のようにエラーが返る。

2019/08/01 localStorageを管理するclass

Storage.js

内部の値は空のオブジェクトから初めて、初期化時にローカルストレージの値に上書きする。
インスタンス時の引数に値が上書きされる。初期値とローカルストレージの値をマージした方がいい?

2019/07/11 box-shadowでborder-bottomを代替する

borderの見た目をposition: absolute;で配置するようなイメージ。
以下のような条件の場合に使うといい。

  • ::before::afterは使っている
  • padding-bottom;で余白を入れてうえでborder-bottomを指定したい
  • 擬似要素をposition: absolute;で配置しているので、padding-bottom;を指定するとズレてしまう
.Heading {
  box-shadow: 0px 10px 0px 0px #fff, 0px 12px 0px 0px #aaa;
}
  • カンマで区切ると複数指定ができる
  • 上記だと2つ目でborder-bottom: 12px solid #aaa、1つ目でborder-bottom: 10px solid #fffを指定しているイメージ
  • 指定は最初の方から優先されるため、2つ目のグレーの上に1つ目の白が被さるような表現になる

注意点

  • borderと違い、box-shadowはボックスモデルに含まれない
  • 下側にbox-shadowを入れたら、margin-bottomにはbox-shadow分を足した値にする必要がある

box-shadow - CSS: カスケーディングスタイルシート | MDN

2019/08/02 lodash-esのimportを最適化する

必要なモジュールだけをインポートするのが最適解とはいえ、楽をしたいのもの。
Tree Shakingでいい感じに最適化してくれないかなとテストをした。

すべて_.map();と同等の処理をしている。

import ファイルサイズ
import _ from 'lodash-es'; 96KB
import { map } from 'lodash-es'; 16KB
import * as _ from 'lodash-es'; 25KB

意外なことに、import * as _ from_にモジュールをすべて含めるパターンが{ map }のように個別にインポートするパターンとそれほど大差がない。

_.cloneDeep();_.omit();を追加して再テストした。

import ファイルサイズ
import _ from 'lodash-es'; 96KB
import { map, cloneDeep, omit } from 'lodash-es'; 23KB
import * as _ from 'lodash-es'; 32KB

一括 / 個別 *100%で計算すると、_.map()だけの場合は156%大きかったのが、3つになると139%になっていた。
実数だと1つだけの場合と3つの場合で共に9KBしか変わらない。
合計6つにしても10KB(64と74)しか変わらない。

一括でインポートする場合もTree Shakingの対象になっていて、10KB増えているのは_プロパティにlodashをインポートする処理なのではないか。

import * as _ from 'lodash-es';を使うのが、パフォーマンスを含めた最適解かもしれない。

気になったのはモジュールを6つ使って64KBって、フルサイズの96KBとそんなに変わらないのでは…。

lodashをフルで入れた場合で80KBだった。
もうlodashでいいのではという気もしてきた。
debounceとthrottleくらいしか使ってない場合はlodash-es、値を色々フォーマットする場合はlodashを使うのがよさそう。

2019/09/26 ウィジウィグで出力されるHTMLタグ

タグの説明はMDNを参照。
優先度は、1が必須、2が任意、3が不要。

親要素 用途 子要素 優先度 WordPress MT
h1, h2, h3, h4, h5, h6 HTML の h1–h6 要素は、セクションの見出しを6段階で表します。 h1 が最上位で、 h6 が最下位です。 a, codeなど 1
hr HTML の hr 要素は、段落レベルの要素間において、テーマの意味的な区切りを表します。例えば、物語のシーンの切り替えや、節における話題の転換などです。 なし 1
ol HTML の ol 要素は、項目の順序付きリストを表し、ふつうは番号付きのリストとして表示されます。 li, ol, ul 1
p HTML の p 要素は、テキストの段落を表します。 a, img, strongなど 1
ul HTML の ul 要素は、項目の順序なしリストを表します。一般的に、行頭記号を伴うリストとして描画されます。 li, ol, ul 1
a HTML の a 要素 (アンカー要素) は、別のウェブページ、ファイル、同一ページ内の場所、電子メールアドレス、または他の URL へのハイパーリンクを作成します。 なし 1
br HTML の br 要素 は、文中に改行(キャリッジリターン)を生成します。詩や住所など、行の分割が重要な場合に有用です。 なし 1
em HTML の em 要素は、強調されたテキストを示します。em 要素は入れ子にすることができ、入れ子の段階に応じてより強い程度の強調を表すことができます。 なし 1
strong HTML の強い重要性要素 (strong) は、内容の重要性、重大性、または緊急性が高いテキストを表します。ブラウザーは一般的に太字で描画します。 なし 1
img HTML の img 要素は、文書に画像を埋め込みます。これは置換要素です。 なし 1
iframe HTML のインラインフレーム要素 (iframe) は、入れ子になった閲覧コンテキストを表現し、現在の HTML ページに他のページを埋め込むことができます。 なし 1 × ×
table HTML の table 要素は表形式のデータ、つまり、行と列の組み合わせによるセルに含まれたデータによる二次元の表で表現される情報です。 thead, tbody, tfoot, caption, th, td 1 × ×
blockquote HTML の blockquote 要素 (HTML ブロック引用要素) は、内包する要素の文字列が引用文であることを示します。通常、字下げを伴ってレンダリングされます (整形方法については注意の項を参照してください)。 cite 属性により引用元の文書の URL を、 cite 要素により引用元の文書のタイトルなどを明示可能です。 p, cite, footerなど 1
pre HTML pre 要素は、整形済みテキスト (preformatted text) を表します。この要素内のテキストは一般的に、ファイル内でのレイアウトをそのまま反映して等幅 ("monospace") フォントで表示されます。この要素内のホワイトスペース文字はそのまま表示します。 code 1 ×
code HTML の code 要素は、コンピューターコードの短い断片の文字列であると識別できるような外見のコンテンツを表示します。 なし 1 ×
del HTML の del 要素は、文書から削除された文字列の範囲を表します。 なし 1
ins HTML の ins 要素は、文書に追加されたテキストの範囲を表します。 なし 1 ×
b HTML の注目付け要素 (b) は、要素の内容に読者の注意を惹きたい場合で、他の特別な重要性が与えられないものに使用します。 なし 2 × ×
small HTML の small 要素は、テキストのフォントサイズをブラウザーの最小フォントサイズを限度として、一段階縮小します (例えば large から medium へ、 small から x-small へ)。 HTML5 において、表示上のスタイルとは関係なく、この要素は著作権表示や法的表記のような、注釈や小さく表示される文を表すために再提案されました。 なし 2 × ×
sub HTML の 下付き文字要素 (sub) は、表記上の理由で下付き文字として表示するべき行内文字列を指定します。 なし 2 × ×
sup HTML の 上付き文字要素 (sup) は、表記上の理由で上付き文字として表示するべき行内文字列を指定します。 なし 2 × ×
figure HTML の figure 要素は、図表などの自己完結型のコンテンツを表す要素であり、多くの場合、キャプション(figcaption)と一緒に使用され、ふつう1つの単位として参照されます。 img, figcaption 2 × ×
kbd HTML のキーボード入力要素 (kbd) はキーボード、音声入力、その他の入力端末からのユーザーによる文字入力を表す行内の文字列の区間を表します。 kbd 2 × ×
ruby HTML の ruby 要素は、ルビ文字による注釈を表します。ルビ文字による注釈は、東アジアの文字の発音 (振り仮名) を表すためのものです。 rp, rt 2 × ×
s HTML の s 要素は取り消し線を引いた文字列を表示します。 s 要素はすでに適切または正確ではなくなった事柄を表現します。しかし、文書の修正を示す場合、 s 要素は適切ではありません。この場合は del と ins の方が適しているので、こちらを使用してください。 なし 2 × ×
samp HTML のサンプル要素 (samp) は、コンピュータープログラムからのサンプル出力を表す行内文字列を含めるために使用されます。 なし 2 × ×
abbr HTML の略語要素 (abbr) は略語や頭字語を表します。任意で title 属性で、略語の完全形または説明を提供することができます。 なし 3 × ×
i HTML の i 要素は、何らかの理由で他のテキストと区別されるテキストの範囲を表します。例えば、技術用語、外国語のフレーズ、架空の人物の思考などです。英文においてはよく斜体で表示されるものです。 なし 3 × ×
mark HTML の文字列マーク要素 (mark) は、周囲の文脈の中でマークを付けた部分の関連性や重要性のために、参照や記述の目的で目立たせたり強調したりする文字列を表します。 なし 3 × ×
q HTML の q 要素 は、その内容が行内の引用であることを表します。最近の多くのブラウザーでは、文字列を引用符で囲むように実装しています。 なし 3 × ×

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.