Vue.jsで好きなだけ「大石泉すき」とツイートしてみる

この記事は [「大石泉すき」アドベントカレンダー](https://qiita.com/advent-calendar/2019/ohishi-izumi-suki) 12日目の記事です。
今回は重複を気にせず「大石泉すき」とツイートしていきたいと思います。

# 好きなだけ「大石泉すき」とツイートできるページ

こちらからどうぞ。

Tweet 大石泉すき without duplicated entry
[https://ohishi-izumi-suki.herokuapp.com/](https://ohishi-izumi-suki.herokuapp.com/)

ソースコードはこちら。

m19e/non-duplication-tweet
[https://github.com/m19e/non-duplication-tweet](https://github.com/m19e/non-duplication-tweet)

# 諸注意

このページはVue.jsで作ってHerokuでホスティングされていますが、
両方とも細かい説明はしていないので予めご了承ください。

公式サイト↓
[Vue.js - The Progressive JavaScript Framework](https://jp.vuejs.org/index.html)

Herokuへのデプロイはこちらを参考にしました↓
[Vue.jsのアプリケーションを手早くHerokuで公開する] (https://qiita.com/akirakudo/items/f63322f6851feef3d9e4)


# 何をしているのか


templateとmethodsはこんな感じ。

```html:OhishiIzumiSuki.vue
<template>
  <div class="wrapper">
    <h1>Tweet {{ msg }} without duplicated entry</h1>
    <div class="tweet-button-wrapper">
      <a class="tweet-button" @click="setUrl(msg)" :href="url" target="_blank">{{ msg }}</a>
    </div>
  </div>
</template>
```

```javascript:OhishiIzumiSuki.vue
methods: {
    randomRange(start, end) {
      return Math.round(Math.random() * (end - start)) + start
    },
    generateZWSP(width, result = '') {
      if (!width) return result
      return this.generateZWSP(--width, result += '\u200B')
    },
    insertZWSP(text, result = '') {
      if (!text) return result
      result += text[0] + this.generateZWSP(this.randomRange(0, 20))
      return this.insertZWSP(text.slice(1), result)
    },
    countBytes(text) {
      return encodeURIComponent(text).replace(/%../g,"x").length
    },
    setUrl(text) {
      let content = this.insertZWSP(text)
      this.url = "https://twitter.com/intent/tweet?text=" + encodeURI(content + "\nhttps://ohishi-izumi-suki.herokuapp.com")
      console.log(`「${content}」は${content.length}文字(${this.countBytes(content)}bytes)です`)
    },
  },
```

大きく分けて3つのことをしています。

 1. 「大石泉すき」にゼロ幅スペース(ZWSP)を挟む
 - ツイート用URLくっつけてボタンに入れる
 - コンソールに「大石泉すき」の文字数、バイト数を表示する

以上! 簡単ですね。

## 1.ゼロ幅スペースを挟む

### ゼロ幅スペース(zero width space)って？
> ゼロ幅スペース(ゼロはばスペース、英: zero width space, ZWSP)は、コンピュータの組版に用いられる非表示文字で、文書処理システムに対して語の切れ目を示すのに用いる。
Wikipedia - [ゼロ幅スペース](https://ja.wikipedia.org/wiki/%E3%82%BC%E3%83%AD%E5%B9%85%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9)

### 🤔

要するに「**表示されないけどちゃんと存在してる幅のないスペース**」って事です。
**ゼロ幅スペース(以下ZWSP)**を挟むことで見た目は同じテキストでも重複なくツイートする事ができます。

![Screenshot from 2019-12-12 15-06-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408555/e011b46e-c8a6-5059-9672-1f046ffdb99f.png)

画像の通り、見た目は同じでも文字数バイト数が違います。
これで連投しても怒られないぞ。やったね!

```javascript
    randomRange(start, end) {
      return Math.round(Math.random() * (end - start)) + start
    },
    generateZWSP(width, result = '') {
      if (!width) return result
      return this.generateZWSP(--width, result += '\u200B')
    },
    insertZWSP(text, result = '') {
      if (!text) return result
      result += text[0] + this.generateZWSP(this.randomRange(0, 20))
      return this.insertZWSP(text.slice(1), result)
    },
```

 1. randomRange()で0から20までのランダムな数値を取得
 - 数値をgenerateZWSP()に渡してその数だけ結合されたZWSPを返す
 - insertZWSP()でテキストの頭から一文字とって作ったZWSPを結合していく

隙あらば再帰しています。好きなので。

generateZWSP()内の'**\u200B**'が
UnicodeエスケープシークエンスでのZWSPです

Unicodeエスケープシークエンス↓
[#Unicode_escape_sequences 字句文法 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Lexical_grammar#Unicode_escape_sequences)

## 2.ツイート用URLくっつけてボタンに入れる

ツイート用URLは色々できる**Web Intents**で。
[Web Intents - Twitter Developers]( https://developer.twitter.com/en/docs/twitter-for-websites/web-intents/overview)

```html:OhishiIzumiSuki.vue
    <div class="tweet-button-wrapper">
      <a class="tweet-button" @click="setUrl(msg)" :href="url" target="_blank">{{ msg }}</a>
    </div>
```

```javascript:OhishiIzumiSuki.vue
    insertZWSP(text, result = '') {
      if (!text) return result
      result += text[0] + this.generateZWSP(this.randomRange(0, 20))
      return this.insertZWSP(text.slice(1), result)
    },
    setUrl(text) {
      let content = this.insertZWSP(text)
      this.url = "https://twitter.com/intent/tweet?text=" + encodeURI(content + "\nhttps://ohishi-izumi-suki.herokuapp.com")
      console.log(`「${content}」は${content.length}文字(${this.countBytes(content)}bytes)です`)
    },
```

 1. insertZWSP()でテキストにZWSPを挟みこむ
 -  ツイート用URLを作ってthis.urlに代入

setURL()が呼ばれる度にaタグの:hrefが更新され、ツイート内容が変わります。


## 文字数、バイト数を表示

レギュレーションを遵守するべく「大石泉すき」と出力していきます。
大事な情報も一緒に表示してしまいましょう。

```javascript:OhishiIzumiSuki.vue
    countBytes(text) {
      return encodeURIComponent(text).replace(/%../g,"x").length
    },
    setUrl(text) {
      let content = this.insertZWSP(text)
      this.url = "https://twitter.com/intent/tweet?text=" + encodeURI(content + "\nhttps://ohishi-izumi-suki.herokuapp.com")
      console.log(`「${content}」は${content.length}文字(${this.countBytes(content)}bytes)です`)
    },
```

 1. 変数展開してコンソールに出力
 -  バイト数も知りたいのでcountBytes()で数える

![Screenshot from 2019-12-12 17-04-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408555/80317846-4601-53dd-322e-d2bdbed9858f.png)

### 完成!

# 終わりに

当然ながらこのページからのツイートは「大石泉すき」サーチに引っかかりません。
担当サーチの邪魔にもならないので安心して大石泉すきツイートしていきましょう。

![Screenshot from 2019-12-12 17-08-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408555/559d06d6-1ab0-6f8a-eeaf-a714d54e8549.png)

~~あんまり連投するとこわい~~