20201022のvue.jsに関する記事は7件です。

【vue.js】vue-cliとVuetifyでサクッと新規プロジェクトを立ち上げる

ポートフォリオ公開用の簡単なSPAが作りたかったので、
vue-cliを使ってサクッと新規プロジェクトを立ち上げた。

環境

MacOS Catalina 10.15
vue 2.6.12
Node.js 14.2.0

vue-cli導入からプロジェクト作成まで

次のコマンドでvue.cliを導入する。そもそもvue-cliが何かはこちらを参照で。

npm install -g @vue/cli@3.5.0

この記事を書いている今の最新はv4.5.8なのですが、今回はバーション3で進めます。これでインストールは完了。続いてプロジェクトを作成します。

vue create my-portfolio

基本はデフォルトでいいと思いますが、
この段階でTypeScriptだったり、vue-routerを選択する事も可能です。

Vue CLI v3.5.0
? Please pick a preset: 
  Default ([Vue 2] babel, eslint) 
  Manually select features 

バージョンによってはYarnかNPMを選択をさせられる場合がありますが、お好みで。

⚓  Running completion hooks...

?  Generating README.md...

?  Successfully created project my-portfolio.
?  Get started with the following commands:

 $ cd my-portfolio
 $ npm run serve

無事に作成出来ました。早速、npm run serve でローカルで動作確認してみましょ。

スクリーンショット 2020-10-22 23.23.44.png

はい、バチクソ簡単です。

Vuetify入れましょう

今回はサクッとがテーマなので、Vuetifyも使います。
vue-cliが入っていれば、以下コマンドでOKです。

vue add vuetify

これもデフォルトで良いと思います。

✔  Successfully installed plugin: vue-cli-plugin-vuetify

? Choose a preset: (Use arrow keys)
❯ Default (recommended) 
  Prototype (rapid development) 
  Configure (advanced)

npm run serveしてみましょ。

スクリーンショット 2020-10-22 23.32.59.png

OKですね。
ちなみにこの段階でディレクトリ構成はこんな感じ。

スクリーンショット 2020-10-22 23.39.10.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react.js(next.js)とvue.js(nuxt.js)の違い

概要

最近人気のフロントエンドの言語であるreact.js, vue.js, augular.jsがあります
angular.jsは下落傾向で、react.jsvue.jsはほぼ同じように人気があります

この記事では、react.jsvue.jsに対する違いと
next.jsnuxt.jsに対する違いをソースで確認してみましょう

next.jsとnuxt.jsって何?

next.jsreact.jsSSR(ServerSideRender)フレームワークです
nuxt.jsvue.jsSSR(ServerSideRender)フレームワークです

react.jsvue.jsは、基本的にCSR(Client Side Render)です

CSR(Client Side Render)の場合、検索エンジンに露出できない短所があり、
最初のスクリプトロード時間が長いという短所があります

react.jsvue.jsのどちらもBtoBの場合、特別な問題はありませんが、
BtoCの場合、検索エンジンの露出が重要な要素となっているため、
結局はwebpackをカスタマイズするかnext.jsnuxt.jsに変えることになります。

プロジェクト作成の違い

yarn global add create-next-app
create-next-app next-tutorial
yarn dev

next.jsの場合、create-next-appモジュールを利用してプロジェクトを作成します

yarn global add create-nuxt-app
create-nuxt-app nuxt-tutorial
yarn dev

nuxt.jsの場合、create-nuxt-appモジュールを利用してプロジェクトを作成します

component基本

next.jsnuxt.jsはページディレクトリを利用してrouteします
基本的なコンポーネントソースの違いを確認してみましょう

next.js (react.js)

/pages/index.js

import { useEffect } from "react"

const Home = () => {
  useEffect(() => {
    console.log('mounted')
    return () => {
      console.log('unmounted')
    };
  }, []);

  return (
    <div>
      hello next.js
    </div>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    hello nuxt.js
  </div>
</template>

<script>
export default {
  mounted() {
    console.log('mounted')
  },
  unmounted() {
    console.log('unmounted')
  }
}
</script>

react.jsの場合、JSXを使用してレンダリングします
vue.jsの場合、htmlを使用してレンダリングします
ライフサイクルは文法は少し違いますが、構造はほとんど似ています

react.jsの場合、JSXを学ぶ必要があるためパブリッシャーが作業が難しいため、
日本ではvue.jsが人気が高いと言われています

component state

next.js (react.js)

/pages/index.js

import { useState, useEffect } from "react"

const Home = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      count : {count}
    </div>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    count : {{count}}
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

react.jsの場合、react hooksであるuseStateを利用してstateを作成します
vue.jsの場合、データ関数を利用してstateを作成します

react.jsの場合、{}を利用して変数をレンダリングします
vue.jsの場合、{{ }}を利用して変数をレンダリングします

counter

next.js (react.js)

/pages/index.js

import { useState, useEffect } from "react"

const Home = () => {
  const [count, setCount] = useState(0)

  const increaseCount = () => {
    setCount(count + 1)
  }

  const decreaseCount = () => {
    setCount(count - 1)
  }

  return (
    <>
      <div>
        count : {count}
      </div>
      <button onClick={increaseCount}>+</button>
      <button onClick={decreaseCount}>-</button>
    </>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    <div>
      count : {{count}}
    </div>
    <button @click="increaseCount()">+</button>
    <button @click="decreaseCount()">-</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increaseCount() {
      this.count +=  1
    },
    decreaseCount() {
      this.count -= 1
    }
  }
}
</script>

react.jsの場合、setStateを使用してstateをアップデートします
vue.jsの場合、thisを使用してstateをアップデートします

react.jsの場合、コンポーネント内部にイベント関数を作成します
vue.jsの場合、methods内部にイベント関数を作成します

react.jsの場合、onClickプロパティを利用してバインディングします
vue.jsの場合、@clickプロパティを利用してバインディングします

childコンポーネントにsteteとイベント関数を伝達

next.js (react.js)

/components/sample.js

const Sample = (props) => {
  return (
    <>
      <div>
        count : {props.count}
      </div>
      <button onClick={props.increaseCount}>+</button>
      <button onClick={props.decreaseCount}>-</button>
    </>
  )
}

export default Sample

/pages/index.js

import { useState, useEffect } from "react"
import Sample from "../components/Sample"

const Home = () => {
  const [count, setCount] = useState(0)

  const increaseCount = () => {
    setCount(count + 1)
  }

  const decreaseCount = () => {
    setCount(count - 1)
  }

  return (
    <>
      <Sample 
        count={count}
        increaseCount={increaseCount}
        decreaseCount={decreaseCount}
      />
    </>
  )
}

export default Home

nuxt.js (vue.js)

/components/sample.vue

<template>
  <div>
    <div>
      count : {{count}}
    </div>
    <button @click="$emit('increase-count')">+</button>
    <button @click="$emit('decrease-count')">-</button>
  </div>
</template>

<script>
export default {
  props: ['count'],
  emits: ['increase-count', 'decrease-count']
}
</script>

/pages/index.vue

<template>
  <div>
    <Sample 
      :count="count"
      @increase-count="increaseCount()"
      @decrease-count="decreaseCount()"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increaseCount() {
      this.count +=  1
    },
    decreaseCount() {
      this.count -= 1
    }
  }
}
</script>

react.jsの場合、propsを利用してchildコンポーネントにstateやイベント関数を伝達します
vue.jsの場合、各属性を利用してchildコンポーネントにstateやイベント関数を伝達します

react.jsの場合、すぐにバインディングして使用できます
vue.jsの場合、$emitを利用してバインディングすると使用できます

SSR function

next.js (react.js)

/pages/index.js

const Home = ({data}) => {
  return (
    <>
      {data}
    </>
  )
}

Home.getInitialProps = () => {
  return { data: 'SSR' }
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    {{data}}
  </div>
</template>

<script>
export default {
  asyncData() {
    return { data: 'SSR' }
  },
}
</script>

SSRフレームワークの場合、htmlが構成される前にデータをfetchできる関数を提供します

next.jsの場合、getInitialPropsを使用します
nuxt.jsの場合asyncDataを使います

state 管理

vue.jsの場合、vuexを主に使います
vuex.png

react.jsの場合、主にreduxを使用します
redux-logo-landscape.png

まとめ

最も人気のある言語を簡単に2つ比較してみました
ご覧のとおり vue.jsreact.jsもメカニズムは似ています
またnext.jsnuxt.jsもメカニズムが似ています
したがって、一つを勉強すれば他の言語も簡単にできます

皆さんはvue.jsreact.js、どちらが好きですか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react.js(next.js)とvue.js(nuxt.js)の違いをソースで確認してみましょう

概要

最近人気のフロントエンドの言語であるreact.js, vue.js, augular.jsがあります
angular.jsは下落傾向で、react.jsvue.jsはほぼ同じように人気があります

この記事では、react.jsvue.jsに対する違いと
next.jsnuxt.jsに対する違いをソースで確認してみましょう

next.jsとnuxt.jsって何?

next.jsreact.jsSSR(ServerSideRender)フレームワークです
nuxt.jsvue.jsSSR(ServerSideRender)フレームワークです

react.jsvue.jsは、基本的にCSR(Client Side Render)です

CSR(Client Side Render)の場合、検索エンジンに露出できない短所があり、
最初のスクリプトロード時間が長いという短所があります

react.jsvue.jsのどちらもBtoBの場合、特別な問題はありませんが、
BtoCの場合、検索エンジンの露出が重要な要素となっているため、
結局はwebpackをカスタマイズするかnext.jsnuxt.jsに変えることになります。

プロジェクト作成の違い

yarn global add create-next-app
create-next-app next-tutorial
yarn dev

next.jsの場合、create-next-appモジュールを利用してプロジェクトを作成します

yarn global add create-nuxt-app
create-nuxt-app nuxt-tutorial
yarn dev

nuxt.jsの場合、create-nuxt-appモジュールを利用してプロジェクトを作成します

component基本

next.jsnuxt.jsはページディレクトリを利用してrouteします
基本的なコンポーネントソースの違いを確認してみましょう

next.js (react.js)

/pages/index.js

import { useEffect } from "react"

const Home = () => {
  useEffect(() => {
    console.log('mounted')
    return () => {
      console.log('unmounted')
    };
  }, []);

  return (
    <div>
      hello next.js
    </div>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    hello nuxt.js
  </div>
</template>

<script>
export default {
  mounted() {
    console.log('mounted')
  },
  unmounted() {
    console.log('unmounted')
  }
}
</script>

react.jsの場合、JSXを使用してレンダリングします
vue.jsの場合、htmlを使用してレンダリングします
ライフサイクルは文法は少し違いますが、構造はほとんど似ています

react.jsの場合、JSXを学ぶ必要があるためパブリッシャーが作業が難しいため、
日本ではvue.jsが人気が高いと言われています

component state

next.js (react.js)

/pages/index.js

import { useState, useEffect } from "react"

const Home = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      count : {count}
    </div>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    count : {{count}}
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

react.jsの場合、react hooksであるuseStateを利用してstateを作成します
vue.jsの場合、データ関数を利用してstateを作成します

react.jsの場合、{}を利用して変数をレンダリングします
vue.jsの場合、{{ }}を利用して変数をレンダリングします

counter

next.js (react.js)

/pages/index.js

import { useState, useEffect } from "react"

const Home = () => {
  const [count, setCount] = useState(0)

  const increaseCount = () => {
    setCount(count + 1)
  }

  const decreaseCount = () => {
    setCount(count - 1)
  }

  return (
    <>
      <div>
        count : {count}
      </div>
      <button onClick={increaseCount}>+</button>
      <button onClick={decreaseCount}>-</button>
    </>
  )
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    <div>
      count : {{count}}
    </div>
    <button @click="increaseCount()">+</button>
    <button @click="decreaseCount()">-</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increaseCount() {
      this.count +=  1
    },
    decreaseCount() {
      this.count -= 1
    }
  }
}
</script>

react.jsの場合、setStateを使用してstateをアップデートします
vue.jsの場合、thisを使用してstateをアップデートします

react.jsの場合、コンポーネント内部にイベント関数を作成します
vue.jsの場合、methods内部にイベント関数を作成します

react.jsの場合、onClickプロパティを利用してバインディングします
vue.jsの場合、@clickプロパティを利用してバインディングします

childコンポーネントにsteteとイベント関数を伝達

next.js (react.js)

/components/sample.js

const Sample = (props) => {
  return (
    <>
      <div>
        count : {props.count}
      </div>
      <button onClick={props.increaseCount}>+</button>
      <button onClick={props.decreaseCount}>-</button>
    </>
  )
}

export default Sample

/pages/index.js

import { useState, useEffect } from "react"
import Sample from "../components/Sample"

const Home = () => {
  const [count, setCount] = useState(0)

  const increaseCount = () => {
    setCount(count + 1)
  }

  const decreaseCount = () => {
    setCount(count - 1)
  }

  return (
    <>
      <Sample 
        count={count}
        increaseCount={increaseCount}
        decreaseCount={decreaseCount}
      />
    </>
  )
}

export default Home

nuxt.js (vue.js)

/components/sample.vue

<template>
  <div>
    <div>
      count : {{count}}
    </div>
    <button @click="$emit('increase-count')">+</button>
    <button @click="$emit('decrease-count')">-</button>
  </div>
</template>

<script>
export default {
  props: ['count'],
  emits: ['increase-count', 'decrease-count']
}
</script>

/pages/index.vue

<template>
  <div>
    <Sample 
      :count="count"
      @increase-count="increaseCount()"
      @decrease-count="decreaseCount()"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increaseCount() {
      this.count +=  1
    },
    decreaseCount() {
      this.count -= 1
    }
  }
}
</script>

react.jsの場合、propsを利用してchildコンポーネントにstateやイベント関数を伝達します
vue.jsの場合、各属性を利用してchildコンポーネントにstateやイベント関数を伝達します

react.jsの場合、すぐにバインディングして使用できます
vue.jsの場合、$emitを利用してバインディングすると使用できます

SSR function

next.js (react.js)

/pages/index.js

const Home = ({data}) => {
  return (
    <>
      {data}
    </>
  )
}

Home.getInitialProps = () => {
  return { data: 'SSR' }
}

export default Home

nuxt.js (vue.js)

/pages/index.vue

<template>
  <div>
    {{data}}
  </div>
</template>

<script>
export default {
  asyncData() {
    return { data: 'SSR' }
  },
}
</script>

SSRフレームワークの場合、htmlが構成される前にデータをfetchできる関数を提供します

next.jsの場合、getInitialPropsを使用します
nuxt.jsの場合asyncDataを使います

state 管理

vue.jsの場合、vuexを主に使います
vuex.png

react.jsの場合、主にreduxを使用します
redux-logo-landscape.png

まとめ

最も人気のある言語を簡単に2つ比較してみました
ご覧のとおり vue.jsreact.jsもメカニズムは似ています
またnext.jsnuxt.jsもメカニズムが似ています
したがって、一つを勉強すれば他の言語も簡単にできます

皆さんはvue.jsreact.js、どちらが好きですか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue.jsのtips 殴り書き

目次

はじめに
タブをつくる
assets配下の画像ファイルパスの指定方法

はじめに

ここでは、個人的にvue.jsを触って学んだことや覚えておきたいことなどを適宜殴り書きしていくスタイルのページとなります。

タブをつくる

vue.jsでのタブの作り方

template

<ul class="tabs">
  <li
    v-for="(tab, index) in tabs"
    :key="tab.id"
    :class="{ 'is-active': isActive === index }"
    class="seminarSearch_tabItem"
    @click="isActive = index"
  >
    {{ tab.tabName }}
  </li>
</ul>
<div v-show="isActive === 0" class="content">
  <Component />
</div>
<div v-show="isActive === 1" class="content">
  <div>
    <p>ほげほげ</p>
  </div>
</div>
<Component />

を読み込ませることや

<div>
  <p>ほげほげ</p>
</div>

直接htmlを入れることもok.

typescript

import Component from '~/components/presentationals/Component.vue'

export default {
  components: {
    Component,
  },
  data() {
    return {
      isActive: 0,
      id: 0,
      tabs: [
        { id: 1, tabName: 'タブ1' },
        { id: 2, tabName: 'タブ2' },
      ],
    }
  },
}

これでタブが出来上がる。


See the Pen
BazQrYg
by miyawash (@miyawash)
on CodePen.



assets配下の画像ファイルパスの指定方法

直接テンプレートに記述して読み込む場合

<img src="@/assets/sample.png">

v-bindまたはvalueで読み込ませる場合

テンプレート側

<img v-bind:src="src" />
or
<img :src="src" />

typescript側

export default {
  data() {
    return {
      src:  require('@/assets/sample.png'),
    }
  }
}

requireを使って読み込ませる。


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js 3.0 で Google Chat 向けの chrome 拡張を作ってみた!

何を作ったか?

  • Google Chat Threads という Google Chat のための拡張機能を作りました :laughing:
    • 拡張をインストールして Google Chat のルームを開くと、スレッドをリストにして表示してくれます
    • スレッドのタイトルを押すことでそのスレッドまでリンクさせることができます

extension.jpg

Google Chat ユーザーの方はぜひ一度使ってみてください!

どう作ったか?

  • 拡張機能の表示領域については、Vue.js 3.0 を使ってみました!
  • Google Chat 上の DOM 要素の取得などは普通の JavaScript で書いています

GitHub のリポジトリを載せておくので、よければ参考にしてください。

https://github.com/Nag729/chat-thread-extension

Vue.js 3.0 で chrome 拡張を作る方法

この記事の本題です。

  • 前提として、VueCLI でアプリケーションを作ったことがあるくらいのレベルを想定しています
  • chrome 拡張の基本的な作成方法について体系的な説明はしていません よく分かっていないので

:link: Link

Vue.js × chrome拡張 では、以下の記事が大変参考になりました。
この記事では Vue 2.x で実装されていますが、基本的な書き方はほとんど変わらなかったです。

:wrench: プロジェクトの作り方

:star: Vue.js を chrome 拡張でどう動かすか

manifest.json の定義

chrome 拡張では manifest.json というファイルに定義された JS / CSS ファイルが動作する という仕組みになっています。
今回の拡張では、以下のような指定をしました。

:star: manifest.json

{
  ...
  "content_scripts": [
    {
      "matches": ["https://chat.google.com/*"], // Google Chat のページで有効化
      "js": ["js/chunk-vendors.js", "js/app.js"], // ビルドされたJSファイルを指定
      "css": ["css/app.css"], // ビルドされたCSSファイルを指定
      "run_at": "document_end" // ページが読み込み完了したタイミングで拡張を動かす
    }
  ],
  ...
}

VueCLI でプロジェクトを作ったことがあれば何となく想像がつくと思いますが、
npm run build で出力されるファイルを読み込むように manifest.json を定義しています。

dist/ に動作する chrome 拡張フォルダを作るための方法

npm run build で出力されるのは Vue のソースだけなので、chrome 拡張として動作させるためにはいくつかのファイルのコピー・削除等が必要です。
その辺りの操作は、npm scripts で解決しています。

:star: package.json

...
"scripts": {
  "compile:content": "vue-cli-service build --no-clean src/main.js", // Vue build
  "compile": "run-s compile:content",
  "copy:image": "cpx src/assets/48.png dist/", // 拡張のiconをコピー
  "copy:manifest": "cpx src/manifest.json dist/", // manifest.jsonをコピー
  "copy": "run-p copy:manifest copy:image",
  "remove": "rimraf dist/index.html", // index.htmlは不要なので削除
  "build": "run-s compile copy remove" // 上記のscriptsをまとめて呼び出す
},
...

また、VueCLI はデフォルトだとファイル名にハッシュを付けるので、vue.config.js を編集します。
ついでに、sourceMap も不要なので false にしておきます。

:star: vue.config.js

module.exports = {
  filenameHashing: false,
  productionSourceMap: false,
};

main.js の定義

Vue インスタンスを作成するための main.js についても簡単に説明しておきます。
画面の DOM 要素を取得して <div> タグを挿入し、挿入したタグに Vue をマウントしています。

:star: main.js

import { FontAwesomeIcon } from "@/plugins/font-awesome";
import { createApp } from "vue";
import App from "./App.vue";

const extensionStart = () => {
  // DOM を更新
  const element = document.querySelector("body > div");

  // メインの div に Vue を読み込ませる用のタグを追加
  const createdDom = document.createElement("div");
  createdDom.id = "thread-extension";
  element.insertAdjacentElement("beforeend", createdDom);

  // Vue インスタンスの生成
  createApp(App).component("fa", FontAwesomeIcon).mount("#thread-extension");
};

extensionStart();

ここまで準備すれば、後は通常の Vue と同じように書くことが可能です :tada:
※ただし、元サイトの DOM は Vue で管理されていないので document.querySelectorAll などを利用して要素を取得する必要があります。


終わりに

上記の手順は多少面倒でしたが、慣れている Vue で拡張を作れるのは実装しやすかった & 作っていて楽しかったです!
今回のものは小規模ですが、もう少し大規模になってくると Vue のコンポーネント分割の恩恵も受けられると思います。

もしより良い導入方法/実装方法などがあればコメント等で教えていただけると嬉しいです :blush:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue3 で Google Chat 向けの chrome 拡張を作ってみた!

何を作ったか?

  • Google Chat Threads という Google Chat のための拡張機能を作りました :laughing:
    • 拡張をインストールして Google Chat のルームを開くと、スレッドをリストにして表示してくれます
    • スレッドのタイトルを押すことでそのスレッドまでリンクさせることができます

extension.jpg

Google Chat ユーザーの方はぜひ一度使ってみてください!

どう作ったか?

  • 拡張機能の表示領域については、Vue.js 3.0 を使ってみました!
  • Google Chat 上の DOM 要素の取得などは普通の JavaScript で書いています

GitHub のリポジトリを載せておくので、よければ参考にしてください。

https://github.com/Nag729/chat-thread-extension

Vue.js 3.0 で chrome 拡張を作る方法

この記事の本題です。

  • 前提として、VueCLI でアプリケーションを作ったことがあるくらいのレベルを想定しています
  • chrome 拡張の基本的な作成方法について体系的な説明はしていません よく分かっていないので

:link: Link

Vue.js × chrome拡張 では、以下の記事が大変参考になりました。
この記事では Vue 2.x で実装されていますが、基本的な書き方はほとんど変わらなかったです。

:wrench: プロジェクトの作り方

:star: Vue.js を chrome 拡張でどう動かすか

manifest.json の定義

chrome 拡張では manifest.json というファイルに定義された JS / CSS ファイルが動作する という仕組みになっています。
今回の拡張では、以下のような指定をしました。

:star: manifest.json

{
  ...
  "content_scripts": [
    {
      "matches": ["https://chat.google.com/*"], // Google Chat のページで有効化
      "js": ["js/chunk-vendors.js", "js/app.js"], // ビルドされたJSファイルを指定
      "css": ["css/app.css"], // ビルドされたCSSファイルを指定
      "run_at": "document_end" // ページが読み込み完了したタイミングで拡張を動かす
    }
  ],
  ...
}

VueCLI でプロジェクトを作ったことがあれば何となく想像がつくと思いますが、
npm run build で出力されるファイルを読み込むように manifest.json を定義しています。

dist/ に動作する chrome 拡張フォルダを作るための方法

npm run build で出力されるのは Vue のソースだけなので、chrome 拡張として動作させるためにはいくつかのファイルのコピー・削除等が必要です。
その辺りの操作は、npm scripts で解決しています。

:star: package.json

...
"scripts": {
  "compile:content": "vue-cli-service build --no-clean src/main.js", // Vue build
  "compile": "run-s compile:content",
  "copy:image": "cpx src/assets/48.png dist/", // 拡張のiconをコピー
  "copy:manifest": "cpx src/manifest.json dist/", // manifest.jsonをコピー
  "copy": "run-p copy:manifest copy:image",
  "remove": "rimraf dist/index.html", // index.htmlは不要なので削除
  "build": "run-s compile copy remove" // 上記のscriptsをまとめて呼び出す
},
...

また、VueCLI はデフォルトだとファイル名にハッシュを付けるので、vue.config.js を編集します。
ついでに、sourceMap も不要なので false にしておきます。

:star: vue.config.js

module.exports = {
  filenameHashing: false,
  productionSourceMap: false,
};

main.js の定義

Vue インスタンスを作成するための main.js についても簡単に説明しておきます。
画面の DOM 要素を取得して <div> タグを挿入し、挿入したタグに Vue をマウントしています。

:star: main.js

import { FontAwesomeIcon } from "@/plugins/font-awesome";
import { createApp } from "vue";
import App from "./App.vue";

const extensionStart = () => {
  // DOM を更新
  const element = document.querySelector("body > div");

  // メインの div に Vue を読み込ませる用のタグを追加
  const createdDom = document.createElement("div");
  createdDom.id = "thread-extension";
  element.insertAdjacentElement("beforeend", createdDom);

  // Vue インスタンスの生成
  createApp(App).component("fa", FontAwesomeIcon).mount("#thread-extension");
};

extensionStart();

ここまで準備すれば、後は通常の Vue と同じように書くことが可能です :tada:
※ただし、元サイトの DOM は Vue で管理されていないので document.querySelectorAll などを利用して要素を取得する必要があります。


終わりに

上記の手順は多少面倒でしたが、慣れている Vue で拡張を作れるのは実装しやすかった & 作っていて楽しかったです!
今回のものは小規模ですが、もう少し大規模になってくると Vue のコンポーネント分割の恩恵も受けられると思います。

もしより良い導入方法/実装方法などがあればコメント等で教えていただけると嬉しいです :blush:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nuxtのredirectまとめ

まとめ

nuxtのライフサイクル(asyncData、fetch、plugins、middleware)で使える
redirectメソッド(Nuxt 2.14.5時点)のまとめです。

redirectの動作仕様

redirectの書き方

  fetch({ redirect }) {
    redirect([status,] path [, query])
  }

のような書き方で,
ユーザーをpathで示されたルートにリダイレクトさせることができます。

status: 型 Number デフォルト302 (SSRの時の、レスポンスのステータスコードを指定する引数)
path: 型 String or Object (リダイレクトさせるURLを指定する引数)
query: 型 Object (URLクエリパラメータ を指定する引数)

pathはいろいろな形で指定できます。
1. スキーム, ホスト名, ドメイン名を含んだURL文字列
ex) path = https://www.google.co.jp/webhp
2. ドメイン無しの絶対パスや相対パスの文字列
ex) path="/users", path="./users"
3. vue routerのrouter.push({path: "/users"})で使われるようなLocationオブジェクト
ex) path={ path: "users/1"}, path={ name: 'user', params: { userId: '123' } }

Locationオブジェクトの詳細
https://router.vuejs.org/ja/guide/essentials/navigation.html#router-push-location-oncomplete-onabort

SSR, CSR と pathの引数の指定別のredirectの動作

nuxtはSever Side Rendering(SSR)とClient Side Rendering(CSR)で動作が微妙に変わったりするので、分けて考えます。

SSRの場合は、300系のステータスコードとLocationヘッダで、リダイレクトさせます。

CSRの場合は、
外部URLなら、汎用ページ遷移Javascript関数のwindow.location.replace()
内部URLなら、vue routerの関数のrouter.push()
よしなにページ遷移させます。

SSR CSR
1 フルURL HTTPレスポンスが引数statusのコードで返り、pathのURLへリダイレクト window.location.replace(path)でURLが置き換わる
2 絶対パス・相対パス HTTPレスポンスが引数statusのコードで返り、pathのURLへリダイレクト router.pushでpathのURLへ遷移
3 Locationオブジェクト HTTPレスポンスが引数statusのコードで返り、Locationオブジェクトの表すURLへリダイレクト router.pushでLocationオブジェクトの表すURLへ遷移

所感

redirectのpath引数でvue routerのLocationオブジェクトが取れること、内部URLの場合、router.pushも使ってくれるところが盲点でした。
今まで、redirectはrouter.pushを使ってくれない。なのでredirectを使ったら、必ずSSRになると勘違いしていたため、redirectあまり使いたくないと思っていたのですが、普通に使えそうです。

ページの初期のデータ読み込みとリダイレクトの処理は、
nuxtライフサイクルでもvueライフサイクルのどちらでも書けるので、
割と混沌になりやすいと個人的に思っていたのですが、
ここら辺の処理はnuxtライフサイクルのみで完結させるのが、
nuxtの想定しているやり方、そして割とすっきり書けるやり方なのかもしれません。

参考

公式のドキュメントだとbeta版のGUIDEがredirect周りを詳しく書いてます。
https://ja.nuxtjs.org/guides/internals-glossary/context#redirect

あとはひたすらnuxt公式のコードリーディングです。
https://github.com/nuxt/nuxt.js/

このファイル
app.context.redirect =あたりの記述から、
pathの指定として、1・2・3のようなパターンがあること、
app.context.next()でページ遷移処理らしきことやっていることが分かりました。

そしてこのファイル
const next = ssrContext ? ssrContext.next : location => app.router.push(location)あたりの記述から
app.context.next()は、SSRとCSRで違うページ遷移処理をしていること、
CSRはおそらくapp.router.push(location)でページ遷移処理をしていることが分かりました。

最後にこのファイル
const createNext =あたりの記述から
app.context.next()はSSRでは, 300系のステータスコードとLocationヘッダで、リダイレクト処理をしていそうなことが分かりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む