くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

Vue

v-calendarのdate-pickerで外から表示する月を変える

Vueのコンポーネントで、v-calendarっていうやつがある。

相当色んな事が出来て、今まで見てきたカレンダー的モジュールの中では一番じゃないだろうかと個人的に思ってる。

中のソースもめっちゃ勉強になる。

で、これのDatePickerを使う場合に、外から日付を渡して表示する場所を変えたかったりする。

一筋縄でいかなかったのでちょっと記載。


多分普通に使う分には↓の感じ。

<vc-date-picker v-model="dateval" />

適当なdataをv-modelで指定してあげれば、それに応じた日付にはしてくれると思う。

ただ、このdatevalを外で変えた場合に、カレンダーのページがその日付の月になってない。

これをやるのに、shoPageRangeっていうの呼ぶらしい。公式のドキュメントにもそんな感じで書いてある。

これのissue

まー上手くいかないと。popoverの場合は上手くいかない。is-inlineだとうまくいくっぽい。

どうもpopoverの場合、$refs.calendarの$refsがcalendarじゃなくてpopoverになってるみたいですね。

で、ソース色々見てたら、少なくとも最新のバージョンではどうも違うっぽい。正解はこっちだと思う。

<vc-date-picker v-model="dateval" ref="calendar"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
    this.$refs.calendar.adjustPageRange()//コッチ
  }
}

datepicker使う場合は少なくとも「adjustPageRange」っぽいですね。

内部的なカレンダーの日付のページにこれで合わせてくれる模様。

コード的にはここ

あと、カレンダーが非表示の状態だと動かないっぽい?

なので、最終的にはこんな感じにしておく必要がありそう。

<vc-date-picker v-model="dateval" ref="calendar" @popoverDidShow="handleShow"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
  },
  handleShow(){
    カレンダーが表示されたときに呼ぶ
    this.$refs.calendar.adjustPageRange()
  }
}

StoryBookのVisualTest(StoryShotsとStorycap)

以前書いたやつの続き。

StoryBookでビジュアルリグレッションテストしたいときの構成。

前のはURL単位にテストファイルを作っていたのですが、一括で全URL対象にした方が楽。

storyshotsを使うかscreencapを使うか。

両方やってみてscreencapを使うようにしたのでその辺の話。


StoryShots

GitHubのReadmeを見れば導入はそのままでOK。

で、実際に使う時に設定したこと。

除外したいstoryの指定

実際のコンポーネントを考えると、ずっとアニメーションしてるものの場合、同じスクリーンショットとるとか不可能なので、テストを外したい。Readmeにも書いてあるけどその場合は↓。

import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex:/^((?!.*?DontTest).)*$/
});
スナップショット取る前にちょっと待機させたい場合

開いた瞬間アニメーションしてるやつが落ち着くまで待機させたい場合。

import initStoryshots from '@storybook/addon-storyshots'
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer'


const beforeScreenshot = (page, { context: { kind, story }, url }) => {
  return new Promise(
    (resolve) =>
      setTimeout(() => {
        resolve()
      }, 1000) // waitのミリ秒
  )
}

initStoryshots({
  suite: 'Image',
  storyKindRegex: /^(?!.*?nvis).*$/,
  test: imageSnapshot({
    storybookUrl: 'http://localhost:9009',
    beforeScreenshot,
  }),
})

で、見逃してるだけのような気もするのですが、story毎に待機時間を決められないように思うので、待機時間つけちゃうと全部遅くなっちゃう。。。

あと、除外する名前の正規表現ってURLのところになってると思うのですが、どうにも上手くいかない。名称の見方がなんか違うっぽい。どう書けばいいんだろうか。正規表現的に色々試してみたのですが、どうもやりたい事が出来ない。


StoryCap

GitHubのリポジトリ。

で、StoryShotsの代わりにこれを使うようにした。スターの数だと圧倒的にStoryShotsなんだけど、

こっちの方が望む結果にすぐたどり着いたので。導入もすごく楽。

動き的にHTMLの変化を監視して、画面のアニメーションが落ち着くのを待ってからスクリーンショットを撮ってるぽい。賢い。

もちろんずっとアニメーションしてるコンポーネントは↑のWaitのタイムアウトでエラーになるけど。

で、そういうコンポーネントを除外したい場合は↓の感じで実行コマンドに渡す。

storycap http://localhost:9009 -e "**/DontTest/**"

ルールとしてstorybookに表示されるサブディレクトリの階層で、DontTestとか階層を作って、テスト除外したい奴はそこに配置する感じ。ちょっとカッコ悪いけど、urlをminimatchしてるのかな?

ディレクトリ階層で切り分けないとうまくいかなかった。

ただし、StoryCapはスクリーンショットを撮るだけでテストまでは入ってないので、

そこは自分で書いておかないとダメ。ロードマップ見ると将来的には対応してくれるようす。

テストは↓の感じで書けばよいのではないかと。

const fs = require('fs')

function getFiles(dir, files_) {
  files_ = files_ || []
  const files = fs.readdirSync(dir)
  for (const i in files) {
    const name = dir + '/' + files[i]
    if (fs.statSync(name).isDirectory()) {
      getFiles(name, files_)
    } else {
      files_.push(name)
    }
  }
  return files_
}

const files = getFiles('storycapで画像が出来るディレクトリ')

describe.each(files)('ビジュアルテスト', (filepath) => {
  test(`file: ${filepath}`, () => {
    const data = fs.readFileSync(filepath)
    expect(data).toMatchImageSnapshot({
      customSnapshotsDir: 'テスト用のスナップショット周りが出来るディレクトリ',
      failureThreshold: 0.10,
      failureThresholdType: 'percent',
    })
  })
})

Vue Render使う時に引数有のClickイベントを定義したい

VueでClickイベントのハンドラを書く時は大体↓の感じでかく。

<button v-on:click="handleClick">click me</button>

methods:{
  handleClick: function(arg){
    this.count++
    this.args = arg
  },
}

で、引数に何か渡したい場合は↓

<button v-on:click="handleClick(1, $event)">click me</button>

methods:{
  handleClick: function(arg, event){
    this.count++
    this.args = arg
  },
}

通常のeventオブジェクトも↑の感じで渡せる。

で、これをRender使ったときにどう書くかちょっとハマったので記載。

調べても出てこないのよね。みんなこの程度の事にはハマってすらいないんだろうなーと

ちょっと寂しい気持ちになりました。頭悪くてごめんなさい。

で、renderで書く場合、onのハンドラは下の感じでcreateElementに渡す。

createElement('button', {on: {click: handleClick}}, 'ボタン')

普通に引数付きで書くとこう書いちゃう。

createElement('button', {on: {click: handleClick(1)}}, 'ボタン')

コレだとダメ。

オブジェクトはあくまで関数を渡さないといけないので、↑だと関数を呼んじゃってる。

なので、下の感じにする。

createElement('button', {on: {click: (x) => this.handleClick(1, x)}}, '引数とイベント有')

ラムダ式の引数がイベントオブジェクトになってるのでそれをそのまま渡すと。

ハマったハマった。。。

サンプルは↓。

Vue + Jestが動かない

Vueコンポーネントの単体テストをJest使ってやってたんですが、

別の端末でCloneして動かしたらなんか動かない。

エラーは下の感じ。

Configuration contains string/RegExp pattern, but no filename was passed to Babel

うーん。デバッグしてみたりもしたけど意味不明。

何じゃらほい。と思って、とりあえず「node_modules」ディレクトリを丸ごと消して、

「package-lock.json」ファイルを消して再度「npm install」したら治った。

なんだろね。。。

Vueのrender(描画関数)でJSXでv-modelを使いたい

Vueは素晴らしいと思うのですが、公式のこの辺に書いてある通り、大量なv-ifとか使わないとつらい場合等、純粋なJavaScriptのロジックで書きたくなる事はやっぱある。

その時の対応としてrender()をVueで用意してくれているのですが、いかんせん、なかなか書きにくい感じになっている。それにも対応してくれていて、JSXで書けるようにbabelのプラグインがある。

で、JSXで書けるんだけど、v-modelがまるで効かない。公式的にはv-modelは対応してないから自分でロジック作れって書いてはある。でもやっぱVueで入力系のコンポーネント作るときはv-model使いたい。

探してみると、babel-plugin-jsx-v-modelっていうプラグインで出きるっぽい、入れてみようかなーで公式のVue-Cliのソースとかみてたら↓の状態になってた。

sample

おや?つーか、そもそもプラグイン入ってるの?つか、コメントアウトされておる。。。

そしたら、Readmeに↓で書いてあった。

sample2

あらん。Babel7への対応が出来てないから一時的に無効にするとさ。残念。

復活を期待しつつ、v-modelを別の形で実装しておくしかあるまい。

Vue CLI 3 のWebPackの設定

ちょっと独り言。

公式のココに書いてあるけど、vue cli3のinspectが凄く便利だった。

vue inspect > output.js

で、vue cliで作ったプロジェクトのwebpackの設定が全部見れる。

「@」ってaliasでそういう設定してたのねとか。

設定の上書きするのにもこれ見ないとどうすればいいかわかんないですね。

babel thisがundefinedになるやつ

GitHubのリポジトリにあるソースを参考にして、

別のプロジェクトに組み込もうとかした時に、

ちょっと昔のソースだと即時関数的に↓の感じのコードになってる場合。

(function(root,factory){
  //code
}(this,function(){
  //code
}));

bable7以上だと、引数のthisがundefinedになって動かなくなる。

で、色々対応方法調べてみたら、下のがとりあえずの回避方法。

.babelrcの中に下の感じで対象のjsファイルを書く。

module.exports = {
  overrides: [
    {
      test: "./src/utils/hoge.js",
      sourceType: "script"
    },
    {
      test: "./src/utils/fuga.js",
      sourceType: "script"
    }
  ]
};

デフォだとmoduleとして読み込んでるみたいで、export defaultで定義しておかないとダメになるって事なんかな?で、それをmoduleじゃなくてscriptファイルとして読み込めっていう設定なのかしら。いまいち分かってない。

本来はソースをexport default的なやつに直した方がいいんじゃないかと思う。今時の作り方で言えば、即時関数自体やめた方がよいのかしらね。

Vueで「babel.config.js」使ってる場合も同様に下の感じで書く。

module.exports = {
  presets: ["@vue/app"],
  overrides: [
    {
      test: "./src/utils/hoge.js",
      sourceType: "script"
    },
    {
      test: "./src/utils/fuga.js",
      sourceType: "script"
    }
  ]
};

Webシステムのスクリーンショットをjavascriptとか使ってとる

Webの業務システムなんかを作ってるとシステムのスクリーンショットを撮りたいっていう要望がだいたいあがる。PrintScreenでがんばってで終わればいいんだけど、システムの中でやってとか、まーお客さんの環境によってはキャプチャが動かせなかったり色々あるので、システムでやらないとまずい場合がある。あと、スクロールするページの場合、一回のスクショで全体を撮りたいみたいな要望もあったりする。

そん時にどうやるかという事。VueとかNuxtで作ったSPAなアプリとかでやりたい場合とかも。

最近のやり方としては↓の感じなのかなーと思う。

  1. ブラウザのネイティブの機能を使う
  2. javascriptのライブラリを使う
  3. サーバ側で処理する

ブラウザのネイティブの機能を使う

これはChrome前提。自分は使ったことないんだけど、↓あたりのものを使えばいけそう。

Chrome拡張を使うか自分で作ってやる感じのやり方。

javascriptのライブラリを使う

javascriptでやる場合はだいたい下記を使うんじゃないだろうか。

htmlをcanvasにレンダリングするのをjavascriptで作った変態(褒め言葉)モジュール。スターの数とかえらいことになってるの。相当使えるんだけど、100%の再現率じゃない。対応してないstyleの属性とかもあって、ブラウザで見えてるものとはちょっと変わっちゃう。スクショの用途にもよるけど、ある程度の再現が出来てない場合は使えない。あと、ここ半年くらい更新されて無いのでちょっと心配。

でも、特定のDOMだけレンダリングしないとか出来て、例えば個人情報的にこの場所は非表示にしてスクショ撮りたいとかの要望にも対応出来たりする。

サーバ側で処理する

で、自分はこれがいいと思う。サーバにブラウザで表示されてるHTMLのDOMを丸ごと送って、サーバ側で、サーバのchromeなんかで、レンダリングしてスクショ撮る感じ。

今見えてる状態のHTMLを送りつけるので、VueとかNuxtとかのSPAでもちゃんと上手くいく。

で、ブラウザのエミュレータみたいなものが必要なんだけど、最近だとpuppeteer っていうのがすごく使える。Googleが作ってるOSSのものらしいですよ。

スクショ撮った後に、パスワードかけて圧縮してDLさせたりとか色々制御できるのでこのやり方が一番融通効くと思う。

で、やり方。

クライアント側
//現在のHTMLを丸ごと取得
const htmldoc = document.documentElement.cloneNode(true)
//スクリプトタグが邪魔なので全部消す
;[...htmldoc.querySelectorAll('script')].forEach(e => e.remove())
//HTML文字列を取得
const htmlstr = htmldoc.outerHTML
//サーバに送信
this.$axios
.post('http://hoge.fuga/api/save', { data: htmlstr })
.then(function() {
	console.log('OK')
})

サーバにHTMLの文字列丸ごと送ってサーバ側のローカルに保存。そしたらパペティア動かす。

ローカルのファイルを見る場合は↓の感じでやる。

サーバ側

"use strict";
const puppeteer = require("puppeteer");

(async () => {
  //centosで動かす場合は--no-sandboxのオプションをつけないと動かない
  const browser = await puppeteer.launch({
    args: ["--no-sandbox", "--disable-setuid-sandbox"]
  });
  //ブラウザの定義
  const page = await browser.newPage();
  //画面の大きさ指定
  page.setViewport({ width: 1600, height: 900 });
  //保存してあるファイルを読み込み
  await page.goto("file:///home/hogeuser/saved.html", {
    waitUntil: "networkidle0" //遅延ロード
  });
  //スクリーンショットをページ全体で取る場合
  await page.screenshot({ path: "example.png", fullPage: true });
  //PDFをA4で作る場合
  await page.pdf({ path: "test.pdf", format: "A4" });
  //ブラウザを閉じる
  await browser.close();
})();

パペティアの方で色々制御したりも出来るのであとは要件しだい。

ついでに、Webフォントとか使ってる場合、font-faceの指定のところとかサーバ側のローカルにしておけばスクショ側でもちゃんとWebフォントが反映される。

パペティア自体はe2eのテストに使ったり、色んなサイトを巡回したりする用途が普通なのかしらね。

ただ、ブラウザの一番外のスクロールなら「fullPage: true」付ければ全画面のスクショ撮ってくれんるんけど、中のDOMのスクロールはさすがに無理なので、必要ならその辺を展開してからサーバに送るとかが必要かな。

Vue CLI3+Jest+単体テスト+StoryBook+VisualTest

この前の続き。

まず、StoryBookを使えるようにする。Vueで使う場合はバージョンによって色々やり方があるっぽくて、情報が錯綜しがちで凄いハマル。今現在(2018/11/24)ではこの感じでやればいけそう。

あと、StoryBookでコンポーネント単位の確認用ページを作り、それを使ってビジュアルリグレッションテストをやれるようにする。これは公式のココに書いてあるやり方で基本はやるんだけど、Vueだと動かなかったのでちょっと変える。ついでに、Jestでコンポーネントの単体テスト出来るようにする。

今回のサンプルの最終系は下記にあげてます。作り方の全容も下記に記載してます。あー、あとCentOS7です。

https://github.com/n79s/vue-cli-storybook-sample

動かすためのポイントだけ記載。

まずVue CLI3でプロジェクト作るインストール。

vue create vue-cli-storybook-sample

#手動セッティングで作る↓
Vue CLI v3.1.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Linter, Unit
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)


#上記の構成で作った場合
#Unitテストは↓のディレクトリで作る
tests/unit/****.spec.js

#Unitテスト実行は↓
npm run test:unit

で、StoryBookは↓の感じでインストール。WebPackの設定がどうのこうのって昔はあったんだけど、今現在ではさくっと動く。

#https://storybook.js.org/basics/guide-vue/
npm install --save-dev @storybook/vue
npm install --save-dev babel-core babel-loader babel-preset-vue

#package.jsonに下記を追記
{
    .....
    "scripts": {
        "storybook": "start-storybook -p 9001 -c .storybook"
    }
    .....
}

#.storybookディレクトリを作って設定ファイルを作る
mkdir .storybook

#.storybook/config.js
import { configure } from '@storybook/vue';
import Vue from 'vue';

function loadStories() {
  require('../stories');
  const req = require.context('../stories', true, /\.stories\.js$/);
  req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

#storybookの実行
npm run storybook

そしたら、ビジュアルリグレッションテストできるようにする。

#必要なパッケージのインストール
npm install --save-dev jest puppeteer jest-puppeteer jest-image-snapshot start-server-and-test

#jest.config.js
module.exports = {
    moduleFileExtensions: ['js','jsx','json','vue'],
    preset: 'jest-puppeteer',
    testRegex: './*\\.test\\.js$',
    setupTestFrameworkScriptFile: './tests/setupVisualTests.js',
    transform: {
        '^.+\\.vue$': 'vue-jest',
        '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
        '^.+\\.js$': 'babel-jest',
    },
    transformIgnorePatterns: ['/node_modules/'],
};

#package.jsonに下記を追記
    .....
    "scripts": {
    "jest:visual": "jest --clearCache && vue-cli-service test:unit -c jest.config-visual.js",
    "test:visual": "start-server-and-test storybook http-get://localhost:9001 jest:visual",
    "jest:visual-update": "jest --clearCache && vue-cli-service test:unit -c jest.config-visual.js --updateSnapshot",
    "test:visual-update": "start-server-and-test storybook http-get://localhost:9001 jest:visual-update"
    }
    .....

で、はまったポイント。

まず、VueCLI3はjestを直で実行するのはサポートしてないから「vue-cli-service test:unit」を使えって、GitHubのIssueに書いてあった(どのIssueだったか見つけられなくなった・・・)。色んなサイトで見てるとだいたい「jest -c ****」みたいなコマンドでやる形になってるので、ここを「vue-cli-service test:unit -c ******」に変えないとだめ。

あと、StoryBookの公式に書いてあるjest.config.jsの記載だと動かないのと、VueCLI3で作られてるjest.config.jsでもダメだった。↓の内容にする

#jest.config.js
module.exports = {
    moduleFileExtensions: ['js','jsx','json','vue'],
    preset: 'jest-puppeteer',
    testRegex: './*\\.test\\.js$',
    setupTestFrameworkScriptFile: './tests/setupVisualTests.js',
    transform: {
        '^.+\\.vue$': 'vue-jest',
        '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
        '^.+\\.js$': 'babel-jest',
    },
    transformIgnorePatterns: ['/node_modules/'],
};

一番、はまったのが、「transform」のとこ。jsをbabel-jestでやるように記載しないとダメっぽい。VueCLI3のIssueに上がってた。ただ、まだ解決してないっぽい。何個か解決策がこのissueに書いてあるんだけど、解決するかは人(環境)によるっぽいですね。ちなみに、これをやってないとテスト実行したときに「SyntaxError: Unexpected token import」がでる。jsファイルをbabelで変換出来てないって事なんかな?あと、transformIgnorePatternsでnode_modulesを除外する設定入れておかないとダメになることもある様子。

issueに書いてある解決策はだいたい↓の感じ。

#transformは↓のどれか。js使えるようにするとの、パスがうまく出来てないかのどっちかかな
'^.+\\.js$': 'babel-jest',
'^.+\\.(js|jsx)?$': 'babel-jest'
'^.+\\.(js|jsx)?$': '<rootDir>/node_modules/babel-jest'

#transformIgnorePatternsは↓のどっちか
transformIgnorePatterns: ['/node_modules/']
transformIgnorePatterns: ['<rootDir>/node_modules/']

#あとはnode_modules一回消してnpm installし直すとか
rm -rf node_modules && npm cache clean --force && npm install

#キャッシュをクリアするとか
"test:unit": "jest --clearCache && vue-cli-service test:unit

自分は通常の単体テストとは分けてjest.config.js作って指定している。あと、jest.config.jsに「preset: 'jest-puppeteer',」入れてないと動かない。あーあと、スナップショットを更新するコマンドは「"jest:visual-update": "jest --clearCache && vue-cli-service test:unit -c jest.config-visual.js --updateSnapshot",」でやる。単体テスト側のスナップショットの更新は↓のコマンド

#単体テスト側のスナップショットの更新は↓(途中の--は間違ってるわけじゃない)
npm run test:unit -- --updateSnapshot

「--」重ねるのがはまった。

次はStoryBookのアドオンを調整するんだけど、StoryBookのReadmeではまった。「webpack.config.js」で最終的に↓の感じにする。

const path = require('path');

module.exports = (storybookBaseConfig, configType, defaultConfig) => {
  defaultConfig.module.rules.push({
    test: [/\.stories\.js$/, /index\.js$/],
    loaders: [require.resolve('@storybook/addon-storysource/loader')],
    include: [path.resolve(__dirname, '../stories')],
    enforce: 'pre',
  });
  defaultConfig.module.rules.push({
    resourceQuery: /blockType=docs/,
    use: [
      'storybook-readme/env/vue/docs-loader',
      'html-loader',
      'markdown-loader',
    ],
  });
  return defaultConfig;
};

で、ビジュアルリグレッションできるようにしたんだけど、自分はコンポーネントはシステム本体とは別プロジェクトにしてやってる。Vuexとか絡むとロジックをかなり書かないといけないので。見た目の変更箇所だけ確認できるようにしたい。SotryBookでその辺は定義して、StoryBookのページを丸ごとGetしてやるだけにする感じ。

普通のe2eのテストでは無く、あくまでも単体テストの一環としてやる感じ。e2eのテストはSeleniumでやる。Chrome拡張で操作記録とってそのまま動かせるので。ただし、操作記録のスクリプトは長い期間保守することを考えるとさすがにイケてないので、調整は必要(要素の指定の仕方とか)。

参考サイト

storybook系
vue-test-utils系
Jest系
GitHubのサンプル
GitHubのissue

Vue CLI3でコンポーネントをnpmのモジュールにする

随分ひさしぶりに書く気がする。まーなんか寝れないので何となく書こう。

Vueでコンポートネントを色々作って行くんだけど、規模が大きくなったり、別のプロジェクトで使いたくなった場合とか、どうにか簡単にやりたい。イメージ的にはelement-ui的に、Vueプラグインとしてインストールしたらすぐ使える的な感じ。npmのモジュールとして使えるようにしたいわけです。

で、これをやるにはWebPackとかBabelとかもろもろ激しく設定しなきゃいけなさそうで、ちょっとゲンナリしてたんだけど、Vue-cli3を使うと割と簡単にできたので、そのやり方。

あと、作ったモジュールはverdaccio(なんて読むの?)を使ってプライベートリポジトリに登録して、ちんまり使うようにする。自分恥ずかしがり屋なもんで。

2019/2/27追記
verdaccio:ヴェルダッチォって発音してるっぽ。イタリア語で「緑で描く」、「黄、黒、緑の土を混ぜたもの」みたいな意味らしいけど、絵画を描くときに塗る下地?の色ってことっぽ。そんな感じのニュアンスなんでしょか。 で、これはカタカナでなんて書けばいいんだろか、ベルダッチョはカッコ悪いしの。ベルダチオでいいか。。。

今回のサンプルは下記に上げてます。

https://github.com/n79s/vue-npm-module-sample

①Vue-Cli3のインスト

npm install -g @vue/cli

②コンポーネント作るようのプロジェクトを用意

vue create my-hello-module

③コンポーネント作る

# 例えばこんな感じ。
<template>
  <section>
    <h1>Hello World !!!</h1>
    <div>Message : {{ msg }}</div>
  </section>
</template>

<script>
export default {
  name: 'HelloX',
  props: {
    msg: String
  }
}
</script>

<style scoped>
h1 {
  margin: 40px 0 0;
  color:red;
}
</style>

④公開用のindex.js

import Vue from "vue"
import HelloX from "./HelloX.vue"
import HelloY from "./HelloY.vue"
import HelloZ from "./helloz/HelloZ.vue"

const MyComponents = {
    HelloX,
    HelloY,
    HelloZ
}

Object.keys(MyComponents).forEach(name=>{
    Vue.component(name,MyComponents[name])
})

export default MyComponents

⑤ビルドの設定

# package.jsonはこんな感じに変える
・・・
  "private": false,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "build-x": "vue-cli-service build --target lib --name my-hello-module src/components/index.js",
    "lint": "vue-cli-service lint"
  },
  "main":"./lib/my-hello-module.common.js",
  "files": [
    "dist/*",
    "lib/*",
    "src/*",
    "public/*",
    "*.json",
    "*.js"
  ],
・・・

# vue.config.js(これは自分で作る

// vue.config.js
module.exports = {
    // options...
    "css":{
        "extract":false
    },
    "outputDir":"lib"
}

package.jsonの"build-x"のところでビルドのコマンド作る。あと、出力先はデフォだとdistだけど、libに変えてる。「vue.config.js」は無くても動くんだけど、細かい設定をしたい場合は必要。で、cssの部分が無いとcssが別ファイルで作成されてしまい、別プロジェクトでインストールしても、cssが適用されず上手くいかないので、上記の感じで書いておかないとダメ。CDN的に公開して、cssは別で読み込んでもらうような場合はいいのかしらね。

⑥ビルド

npm run build-x

ここまでで、libのフォルダが↓の感じになるはず。

sample05

そしたら、verdaccioの準備。

# インストール
npm install --global verdaccio

# 実行
verdaccio

これだけ。デフォで「http://localhost:4873」で動くはず。まず、ユーザ登録する。

npm adduser --registry http://localhost:4873

ID/PASS/Mail入れて終わり。あとは、下記の感じで公開する。

#package.jsonがあるディレクトリで↓を実行
cd my-hello-module
#公開
npm publish --registry http://localhost:4873
#確認
npm info my-hello-module --registry http://localhost:4873

プライベートリポジトリへの登録が終わったので別のプロジェクトを作成して↓の感じの事をやる。

#別プロジェクトを作る
vue create use-hello

#プライベートリポジトリからインストール
npm install my-hello-module --save --registry http://localhost:4873

#main.jsに↓の感じでグローバル登録する
import Vue from 'vue'
import App from './App.vue'

//↓↓↓ここ
import HelloComponents from 'my-hello-module'

Object.keys(HelloComponents).forEach(name=>{
  Vue.component(name,HelloComponents[name])
})
//↑↑↑ここ

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

グローバル登録しておけば、あとはアプリ側で↓の感じで使える。

<template>
  <div id="app">
    <HelloLocal msg="Local"/>
    <HelloX msg="use-hello X" />
    <HelloY msg="use-hello Y" />
    <HelloZ msg="use-hello Z" />
  </div>
</template>

<script>
import HelloLocal from './components/HelloLocal.vue'

export default {
  name: 'app',
  components: {
    HelloLocal
  }
}
</script>

Vue-cli3はかなりいい感じですね。すごく楽。verdaccioも素晴らしい。

あと、StoryBook使って管理してみよ。チームでの作業とか良さげ。

参考

問合せ