Help us understand the problem. What is going on with this article?

Nuxt.js + Element + TypeScript プロジェクト作成

書いてあること

  • Nuxt.js+Element+Typescript環境の構築手順理解メモ

環境

  • CentOS Linux release 8.1.1911 (Core)
  • Node.js v12.16.1
  • Npm 6.14.2
  • Vue 4.2.3
  • Nuxt Cli 2.11.0
  • element-ui 2.4.11
  • @storybook/vue 5.3.17

作成したプロジェクト

↓に置いてあります。
nuxt-typescript-element-project

Nuxt.js

Nuxt.jsプロジェクト作成

下記内容でNuxt.jsプロジェクトを作成

bash
$ npx create-nuxt-app nuxt-typescript-element-project

create-nuxt-app v2.14.0
✨  Generating Nuxt.js project in nuxt-typescript-element-project
? Project name nuxt-typescript-element-project
? Project description Nuxt TypeScript Element Project
? Author name yoshi0518
? Choose the package manager Npm
? Choose UI framework Element
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support
? Choose linting tools ESLint, Prettier
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
⠙ Installing packages with npm

バージョンを確認

下記Webページ左上のバージョンと一致していることを確認
はじめに - NuxtJS

bash
$ npx nuxt -v

@nuxt/cli v2.11.0

ソースフォルダを変更

srcディレクトリをプロジェクトルートに作成

shell
$ mkdir src

下記ディレクトリをsrcディレクトリに移動

  • assets
  • components
  • layouts
  • middleware
  • pages
  • plugins
  • static
  • store
bash
$ mv assets components layouts middleware pages plugins static store ./src

nuxt.config.jsを修正

srcDirを追記

nuxt.config.js
export default {
  mode: 'universal',
+ srcDir: 'src',
  /*
   ** Headers of the page
   */
  head: {
    ・
    ・
    ・
  }

webpack.config.jsを作成

webpack.config.js
const path = require('path')

module.exports = {
  resolve: {
    alias: {
      '~': path.resolve(rootPath, '/src'),
      '@': path.resolve(rootPath, '/src'),
    },
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
}

jsconfig.jsonを修正

jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
-     "~/*": ["./*"],
+     "~/*": ["./src/*"],
-     "@/*": ["./*"],
+     "@/*": ["./src/*"],
      "~~/*": ["./*"],
      "@@/*": ["./*"]
    }
  },
  "exclude": ["node_modules", ".nuxt", "dist"]
}

開発サーバーを起動・確認

bash
$ npm run dev

TypeScript

基本は公式サイトに沿って設定を行う
Setup | Nuxt TypeScript
Runtime(optional) | Nuxt TypeScript

インストールするパッケージ

@nuxt/typescript-build

layouts、components、plugins、middlewaresでTypeScriptを利用するためのパッケージ。
こちらのパッケージに@nuxt/typesは含まれるため、個別インストールは不要。

@nuxt/typescript-runtime

nuxt.config、local modules、serverMiddlewaresのTypeScriptランタイムサポートを提供するパッケージ。

TypeScript Buildを設定

パッケージをインストール

bash
$ npm install --save-dev @nuxt/typescript-build

nuxt.config.jsを修正

buildModules@nuxt/typescript-buildを追加

nuxt.config.js
export default {
  buildModules: [
+   '@nuxt/typescript-build',
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],

型定義ファイルを作成

shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

tsconfig.jsonを作成

公式サイトの掲載内容をそのままコピー後、
baseUrlpathsをソースフォルダ変更に合わせて修正
filesを追加

tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": "./",
    "paths": {
      "~/*": [
        "src/*"
      ],
      "@/*": [
        "src/*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types",
    ]
  },
  "files": [
    "shims-vue.d.ts",
  ],
  "exclude": [
    "node_modules"
  ]
}

TypeScript Runtimeを設定

パッケージをインストール

bash
$ npm install --save @nuxt/typescript-runtime

スクリプトを修正

package.json
{
  "scripts": {
-   "dev": "nuxt",
+   "dev": "nuxt-ts",
-   "build": "nuxt build",
+   "build": "nuxt-ts build",
-   "start": "nuxt start",
+   "start": "nuxt-ts start",
-   "generate": "nuxt generate",
+   "generate": "nuxt-ts generate",
      ・
      ・
      ・
}

nuxt.config.jsを修正

ファイル名をnuxt.config.tsに変更
型指定エラーが表示されるため、extend部分を修正
nuxt.config.ts
export default {
  build: {
    /*
     ** You can extend webpack config here
     */
-   extend(config, ctx) {}
+   extend(config: any, ctx: any) {}
  }
}

typescriptを追加

nuxt.config.ts
export default {
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config: any, ctx: any) {}
  },
+ typescript: {
+   typeCheck: true,
+   ignoreNotFoundWarnings: true
+ }
}

tsconfig.jsonを修正

experimentalDecoratorsを追加

tsconfig.json
{
  "compilerOptions": {
    "types": [
      "@types/node",
      "@nuxt/types",
    ],
+   "experimentalDecorators": true
  },
  "files": [
    ・
    ・
    ・
}

index.vueをクラスベースに修正・動作確認

パッケージをインストール

bash
$ npm install --save-dev nuxt-property-decorator

index.vueを修正

templatestyleは変更無し

src/pages/index.vue(変更前)
<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  }
}
</script>
src/pages/index.vue(変更後)
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator';
import Logo from '~/components/Logo.vue';

@Component({
  components: {
    Logo,
  },
})
export default class Index extends Vue {}
</script>

scriptを追加

src/components/Logo.vue(変更後)
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator';

@Component({})
export default class Logo extends Vue {}
</script>

ESLint、Prettier

パッケージをアンインストール

@nuxtjs/eslint-configは、次の手順でインストールする@nuxtjs/eslint-config-typescriptに含まれるため。

bash
$ npm uninstall @nuxtjs/eslint-config

パッケージをインストール

以降の手順で必要となるeslint-loaderも合わせてインストール。

bash
$ npm install --save-dev @nuxtjs/eslint-config-typescript eslint-loader

.eslintrc.jsを修正

  • parserOptionsを削除
  • extendsから@nuxtjsを削除し、@nuxtjs/eslint-config-typescriptを追加
  • 必要なルールを追加
  • ESLint実行と同時にPrettier(コード整形)を行う設定を追加
.eslintrc.js
module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
- parserOptions: {
-   parser: 'babel-eslint'
- },
  extends: [
-   '@nuxtjs',
+   '@nuxtjs/eslint-config-typescript',
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended'
  ],
  plugins: [
+   'vue',
    'prettier'
  ],
  // add your custom rules here
  rules: {
+   'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+   'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+   'generator-star-spacing': 'off',
+   'prettier/prettier': 'error'
  }
}

package.jsonを修正

lintスクリプトを下記の通り修正

package.json
{
  "scripts": {
-   "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
+   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src",
+   "lint:fix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src --fix",
  },
}

.eslintignoreを作成

.eslintignore
/dist/
/node_modules/
/public/
/*.js
/*.ts
/package.json
/package-lock.json

.prettierrcを修正

各プロジェクトのコーディング規約に応じて修正

.prettierrc
{
- "semi": false,
- "arrowParens": "always",
- "singleQuote": true
+ "trailingComma": 'es5',
+ "printWidth": 140,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "semi: false",
}

.prettierignoreを作成

.prettierignore
/dist/
/node_modules/
/public/
/*.js
/*.ts
/package.json
/package-lock.json

ファイル保存時に自動ESLint

ESLintコマンドを都度実行しなくて済むように、ファイル保存時にチェックがかかるように設定。

nuxt.config.tsを修正

nuxt.config.ts
export default {
  build: {
    /*
     ** You can extend webpack config here
     */
-   extend(config: any, ctx: any) {}
+   extend(config: any, ctx: any) {
+     // Run ESLint on save
+     if (ctx.isDev && ctx.isClient) {
+       config.module.rules.push({
+         enforce: 'pre',
+         test: /\.(js|ts|vue)$/,
+         loader: 'eslint-loader',
+         exclude: /(node_modules)/
+       })
+     }
+   }
  },
}

VSCodeを設定変更

プラグインをインストール

下記2つをインストールする。

設定変更

EditorESLintVeturを下記の通り設定。

setting.json
{
    // Editor
    "editor.formatOnSave": false,

    // ESLint
    "eslint.validate": [
        {
            "language": "vue",
            "autoFix": true
        },
        {
            "language": "javascript",
            "autoFix": true
        },
        {
            "language": "typescript",
            "autoFix": true
        }
    ],
    "eslint.autoFixOnSave": true,

    // Vetur
    "vetur.validation.template": false,
}

Lintコマンドを実行

bash
$ npm run lint:fix

開発サーバーを起動・確認

bash
$ npm run dev

Jest

Jestを設定

パッケージをインストール

bash
$ npm install --save-dev ts-jest @types/jest

jest.config.jsを修正

moduleNameMappermoduleFileExtensionstransformcollectCoverageFromを修正、transformIgnorePatternsを追加

jest.config.js
module.exports = {
  moduleNameMapper: {
-   '^@/(.*)$': '<rootDir>/$1',
+   '^@/(.*)$': '<rootDir>/src/$1',
-   '^~/(.*)$': '<rootDir>/$1',
+   '^~/(.*)$': '<rootDir>/src/$1',
    '^vue$': 'vue/dist/vue.common.js'
  },
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
- moduleFileExtensions: ['js', 'vue', 'json'],
+ moduleFileExtensions: ['js', 'ts', 'vue', 'json'],
  transform: {
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest',
+   '^.+\\.ts$': 'ts-jest',
+   '^.+\\.tsx$': 'ts-jest'
  },
  collectCoverage: true,
  collectCoverageFrom: [
-   '<rootDir>/components/**/*.vue',
+   '<rootDir>/src/components/**/*.vue',
-   '<rootDir>/pages/**/*.vue',
+   '<rootDir>/src/pages/**/*.vue'
  ],
+ transformIgnorePatterns: [
+   '/node_modules/(?!@babel/runtime-corejs2)'
+ ]
}

package.jsonを修正

package.json
{
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
-   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src",
+   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test",
-   "lint:fix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src --fix",
+   "lint:fix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test --fix",
    "test": "jest"
  },
}

tsconfig.jsonを修正

types@types/jestを追加

tsconfig.json
{
  "compilerOptions": {
    "types": [
      "@types/node",
      "@nuxt/types",
      "vuetify",
+     "@types/jest",
    ],
    "experimentalDecorators": true
  },
  "files": [
    ・
    ・
    ・
}

テスト

テストファイルを準備

test/Logo.spec.jsのファイル名をLogo.spec.tsに変更

src/test/Logo.spec.ts
import { mount } from '@vue/test-utils';
import Logo from '@/components/Logo.vue';

describe('Logo', () => {
  test('is a Vue instance', () => {
    const wrapper = mount(Logo);
    expect(wrapper.isVueInstance()).toBeTruthy();
  });
});

テストを実行

bash
$ npm run test

> nuxt-typescript-element-project@1.0.0 test /share/nuxt-typescript-element-project
> jest

 PASS  test/Logo.spec.ts (6.825s)
  Logo
    ✓ is a Vue instance (27ms)

------------|----------|----------|----------|----------|-------------------|
File        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files   |     37.5 |      100 |       50 |    42.86 |                   |
 components |      100 |      100 |      100 |      100 |                   |
  Logo.vue  |      100 |      100 |      100 |      100 |                   |
 pages      |        0 |      100 |        0 |        0 |                   |
  index.vue |        0 |      100 |        0 |        0 |        1,24,25,32 |
------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.218s
Ran all test suites.

Elementコンポーネントのテスト

Elementコンポーネントの読み込み設定

setup.jsを作成
src/test/setup.js
import Vue from 'vue';
import ElementUI from 'element-ui';
import locale from 'element-ui/lib/locale/lang/ja';

Vue.use(ElementUI, { locale });
jest.config.jsを修正

setupFilesAfterEnvを追加

jest.config.js
module.exports = {
  transformIgnorePatterns: [
    '/node_modules/(?!@babel/runtime-corejs2)'
  ],
+ setupFilesAfterEnv: [
+   '<rootDir>/test/setup.js'
+ ],
}

テスト対象のコンポーネントを作成

src/components/Button.vue
<template>
  <el-button type="primary">Button</el-button>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Button extends Vue {}
</script>

テストファイルを準備

src/test/Button.spec.ts
import { mount } from '@vue/test-utils';
import Button from '@/components/Button.vue';

describe('Button.vue', () => {
  it('snapshot', () => {
    const wrapper = mount(Button);
    expect(wrapper.html()).toMatchSnapshot();
  });
});

テスト実行

bash
$ npm run test test/Button.spec.ts

> nuxt-typescript-element-project@1.0.0 test /share/nuxt-typescript-element-project
> jest "test/Button.spec.ts"

 PASS  test/Button.spec.ts
  Button.vue
    ✓ snapshot (101ms)

 › 1 snapshot written.
-------------|----------|----------|----------|----------|-------------------|
File         |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files    |    27.27 |      100 |    33.33 |       30 |                   |
 components  |       50 |      100 |       50 |       50 |                   |
  Button.vue |      100 |      100 |      100 |      100 |                   |
  Logo.vue   |        0 |      100 |        0 |        0 |           1,22,25 |
 pages       |        0 |      100 |        0 |        0 |                   |
  index.vue  |        0 |      100 |        0 |        0 |        1,24,25,32 |
-------------|----------|----------|----------|----------|-------------------|
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 written, 1 total
Time:        4.2s
Ran all test suites matching /test\/Button.spec.ts/i.

jest、ts-jestバージョンによる警告表示

警告が表示されたため下記コマンドでjest、ts-jestそれぞれの最新バージョンを確認・インストール。
テストを実行して警告が表示されないことを確認。

bash
# jestのリリース済バージョンを確認
$ npm info jest versions

# jestの最新バージョンをインストール
$ npm install --save-dev jest@●●●

# jestのリリース済バージョンを確認
$ npm info ts-jest versions

# jestの最新バージョンをインストール
$ npm install --save-dev ts-jest@●●●

# テスト実行
$ npm run test

Storybook

インストール

必要なパッケージをインストール

bash
$ npm install --save-dev @storybook/vue storybook-addon-vue-info @types/storybook__vue @babel/preset-env

ディレクトリを作成

bash
$ mkdir .storybook stories

設定ファイルを作成

.storybook/.babelrc
{
  "presets": ["@babel/preset-env"]
}
.storybook/config.js
import { configure, addDecorator } from '@storybook/vue'
import '!style-loader!css-loader!element-ui/lib/theme-chalk/index.css';
import ElementUI from '@/plugins/element-ui';
ElementUI();

addDecorator(() => ({
  template: `
<div><story/><div>
`
}))

const req = require.context('../stories', true, /\.stories\.ts$/)
const loadStories = () => {
  req.keys().forEach(req)
}

configure(loadStories, module);
.storybook/webpack.config.js
const path = require('path')

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.ts$/,
    exclude: /node_modules/,
    use: [
      {
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/],
          transpileOnly: true
        }
      }
    ]
  })
  config.module.rules.push({
    test: /\.s(a|c)ss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
  })
  config.module.rules.push({
    test: /\.vue$/,
    loader: 'storybook-addon-vue-info/loader',
    enforce: 'post'
  })

  config.module.rules.push({
    test: /\.(otf|eot|svg|ttf|woff|woff2)(\?.+)?$/,
    loader: 'url-loader'
  })

  config.resolve.alias = {
    vue: 'vue/dist/vue.esm.js',
    '@': path.resolve(__dirname, '../src'),
    '~': path.resolve(__dirname, '../src'),
    '@components': path.resolve(__dirname, '../src/components')
  }

  return config
}
package.json
{
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
-   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test",
+   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test ./stories",
-   "lint:fix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test --fix",
+   "lint:fix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ./src ./test ./stories --fix",
    "test": "jest",
+   "storybook": "start-storybook -c .storybook -p 6006"
  },
}

Elementプラグインを修正

src/plugins/element-ui.js
import Vue from 'vue';
import Element from 'element-ui';
import locale from 'element-ui/lib/locale/lang/en';

Vue.use(Element, { locale });


+export default () => {
+  Vue.use(Element, { locale });
+};

Elementコンポーネントを登録

テスト用コンポーネントを作成

src/components/Buttons.vue
<template>
  <div>
    <el-row>
      <el-button>Default</el-button>
      <el-button type="primary">Primary</el-button>
      <el-button type="success">Success</el-button>
      <el-button type="info">Info</el-button>
      <el-button type="warning">Warning</el-button>
      <el-button type="danger">Danger</el-button>
    </el-row>
    <el-row>
      <el-button plain>Plain</el-button>
      <el-button type="primary" plain>Primary</el-button>
      <el-button type="success" plain>Success</el-button>
      <el-button type="info" plain>Info</el-button>
      <el-button type="warning" plain>Warning</el-button>
      <el-button type="danger" plain>Danger</el-button>
    </el-row>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Button extends Vue {}
</script>

ストーリーファイルを作成

stories/Buttons.stories.ts
import { storiesOf } from '@storybook/vue';

import Buttons from '@/components/Buttons.vue';

storiesOf('Element Components', module).add('Buttons', () => ({
  components: { Buttons },
  template: `<Buttons />`,
}));

テスト起動

bash
$ npm run storybook

Storybook

参考

Nuxt,ElementUI,Storybook構成のVueプロジェクトの始め方

yoshi0518
社内SEによる個人メモ。ほぼ自己学習なので間違っていたらすいません。。。
https://www.n-asset.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away