書いてあること
- 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プロジェクトを作成
$ 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
$ npx nuxt -v
@nuxt/cli v2.11.0
ソースフォルダを変更
srcディレクトリをプロジェクトルートに作成
$ mkdir src
下記ディレクトリをsrcディレクトリに移動
- assets
- components
- layouts
- middleware
- pages
- plugins
- static
- store
$ mv assets components layouts middleware pages plugins static store ./src
nuxt.config.jsを修正
srcDirを追記
export default {
mode: 'universal',
+ srcDir: 'src',
/*
** Headers of the page
*/
head: {
・
・
・
}
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を修正
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
- "~/*": ["./*"],
+ "~/*": ["./src/*"],
- "@/*": ["./*"],
+ "@/*": ["./src/*"],
"~~/*": ["./*"],
"@@/*": ["./*"]
}
},
"exclude": ["node_modules", ".nuxt", "dist"]
}
開発サーバーを起動・確認
$ 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を設定
パッケージをインストール
$ npm install --save-dev @nuxt/typescript-build
nuxt.config.jsを修正
buildModules
に@nuxt/typescript-build
を追加
export default {
buildModules: [
+ '@nuxt/typescript-build',
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
型定義ファイルを作成
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
tsconfig.jsonを作成
公式サイトの掲載内容をそのままコピー後、
baseUrl
、paths
をソースフォルダ変更に合わせて修正
files
を追加
{
"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を設定
パッケージをインストール
$ npm install --save @nuxt/typescript-runtime
スクリプトを修正
{
"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部分を修正
export default {
build: {
/*
** You can extend webpack config here
*/
- extend(config, ctx) {}
+ extend(config: any, ctx: any) {}
}
}
typescript
を追加
export default {
build: {
/*
** You can extend webpack config here
*/
extend(config: any, ctx: any) {}
},
+ typescript: {
+ typeCheck: true,
+ ignoreNotFoundWarnings: true
+ }
}
tsconfig.jsonを修正
experimentalDecorators
を追加
{
"compilerOptions": {
"types": [
"@types/node",
"@nuxt/types",
],
+ "experimentalDecorators": true
},
"files": [
・
・
・
}
index.vueをクラスベースに修正・動作確認
パッケージをインストール
$ npm install --save-dev nuxt-property-decorator
index.vueを修正
※template
、style
は変更無し
<script>
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
}
}
</script>
<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
を追加
<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
に含まれるため。
$ npm uninstall @nuxtjs/eslint-config
パッケージをインストール
以降の手順で必要となるeslint-loader
も合わせてインストール。
$ npm install --save-dev @nuxtjs/eslint-config-typescript eslint-loader
.eslintrc.jsを修正
-
parserOptions
を削除 -
extends
から@nuxtjs
を削除し、@nuxtjs/eslint-config-typescript
を追加 - 必要なルールを追加
- ESLint実行と同時にPrettier(コード整形)を行う設定を追加
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スクリプトを下記の通り修正
{
"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を作成
/dist/
/node_modules/
/public/
/*.js
/*.ts
/package.json
/package-lock.json
.prettierrcを修正
各プロジェクトのコーディング規約に応じて修正
{
- "semi": false,
- "arrowParens": "always",
- "singleQuote": true
+ "trailingComma": 'es5',
+ "printWidth": 140,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "semi: false",
}
.prettierignoreを作成
/dist/
/node_modules/
/public/
/*.js
/*.ts
/package.json
/package-lock.json
ファイル保存時に自動ESLint
ESLintコマンドを都度実行しなくて済むように、ファイル保存時にチェックがかかるように設定。
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つをインストールする。
設定変更
Editor
、ESLint
、Vetur
を下記の通り設定。
{
// 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コマンドを実行
$ npm run lint:fix
開発サーバーを起動・確認
$ npm run dev
Jest
Jestを設定
パッケージをインストール
$ npm install --save-dev ts-jest @types/jest
jest.config.jsを修正
moduleNameMapper
、moduleFileExtensions
、transform
、collectCoverageFrom
を修正、transformIgnorePatterns
を追加
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を修正
{
"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
を追加
{
"compilerOptions": {
"types": [
"@types/node",
"@nuxt/types",
"vuetify",
+ "@types/jest",
],
"experimentalDecorators": true
},
"files": [
・
・
・
}
テスト
テストファイルを準備
test/Logo.spec.js
のファイル名を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();
});
});
テストを実行
$ 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を作成
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
を追加
module.exports = {
transformIgnorePatterns: [
'/node_modules/(?!@babel/runtime-corejs2)'
],
+ setupFilesAfterEnv: [
+ '<rootDir>/test/setup.js'
+ ],
}
テスト対象のコンポーネントを作成
<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>
テストファイルを準備
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();
});
});
テスト実行
$ 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それぞれの最新バージョンを確認・インストール。
テストを実行して警告が表示されないことを確認。
# 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
インストール
必要なパッケージをインストール
$ npm install --save-dev @storybook/vue storybook-addon-vue-info @types/storybook__vue @babel/preset-env
ディレクトリを作成
$ mkdir .storybook stories
設定ファイルを作成
{
"presets": ["@babel/preset-env"]
}
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);
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
}
{
"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プラグインを修正
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コンポーネントを登録
テスト用コンポーネントを作成
<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>
ストーリーファイルを作成
import { storiesOf } from '@storybook/vue';
import Buttons from '@/components/Buttons.vue';
storiesOf('Element Components', module).add('Buttons', () => ({
components: { Buttons },
template: `<Buttons />`,
}));
テスト起動
$ npm run storybook