60 실제 적용 후기

source: categories/study/vue-experiance/vue-experiance_9-60.md

60 실제 적용 후기

  1. eslint 기능 강화 - prettier, stylelint
  2. .babelrc: es6 module 문법 CommonJS 문법 변환 방지
  3. .eslintrc: lodash 라이브러리 사용 제한
  4. package.json sideEffects 설정
  5. vuetify tree shaking 적용

0. git hook 자동 설정 안될시 (git hook executable)

chmod ug+x .husky/*
chmod ug+x .git/hooks/*

0-1. eslint, prettier, stylelint 기능 추가함으로써 기대할 수 있는 효과

  1. 코드 스타일 공통화
  2. 리팩토링으로 인한 코드 최적화
  3. vue 관련 준수해야될 사항들 준수하게끔 강제할 수 있음

1. 초기 빌드 용량


2. .babelrc 설정



{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "debug": false
      }
    ]
  ]
}



3. package.json 설정



{
  "name": "",
  "version": "0.1.0",
  "sideEffects": false,
}



4. .eslintrc.js 설정



module.exports = {
	root: true,
	env: {
		node: true,
	},
	extends: [
		'eslint:recommended',
		'plugin:prettier/recommended',
		'prettier',
		'plugin:vue/essential',
		'plugin:vue/recommended',
		'plugin:vue/strongly-recommended',
		'plugin:storybook/recommended',
		'@vue/prettier',
	],
	parserOptions: {
		parser: 'babel-eslint',
	},
	rules: {
		// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		// 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		'no-unused-vars': [
			'error',
			{ vars: 'all', args: 'after-used', ignoreRestSiblings: false },
		],
		'no-restricted-imports': [
			'error',
			{
				name: 'lodash',
				message:
					'lodash has been prohibited due to bundle size. use lodash-es instead.',
			},
		],
		// 'vue/no-unused-components': 'error',
		// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		// 'no-unused-vars': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		// 'vue/no-unused-components': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		// 'no-useless-escape': process.env.NODE_ENV === 'production' ? 'error' : 'off' // 'no-mixed-spaces-and-tabs': process.env.NODE_ENV === 'production' ? 'error' : 'off',
		'prettier/prettier': [
			'error',
			{
				singleQuote: true,
				semi: true,
				useTabs: true,
				tabWidth: 2,
				trailingComma: 'all',
				printWidth: 80,
				bracketSpacing: true,
				arrowParens: 'avoid',
			},
		],
	},
	globals: {
		kakao: false,
	},
};



5. src/plugins/vuetify.js, vue.config.js



import Vue from 'vue';
import Vuetify from 'vuetify/lib';

Vue.use(Vuetify);

export default new Vuetify({
	theme: {
		disable: true,
	},
});




const fs = require('fs');
const path = require('path');
const SpritesmithPlugin = require('webpack-spritesmith');
const ansiRegex = require('ansi-regex');
const autoprefixer = require('autoprefixer');
const filterRules = require('postcss-filter-rules');
const BundleAnalyzerPlugin =
	require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');

module.exports = {
	pluginOptions: {
		i18n: {
			locale: 'en',
			fallbackLocale: 'en',
			localeDir: 'locales',
			enableInSFC: false,
		},
	},
	// configureWebpack: {
	//   entry: ["@babel/polyfill", "./src/main.js"]
	// },
	configureWebpack: {
		plugins: [
			new VuetifyLoaderPlugin(),
			new BundleAnalyzerPlugin({
				openAnalyzer: false,
			}),
			// png 스프라이트 플러그인
			...fs
				.readdirSync(
					path.join(__dirname, 'src/assets/images/sprites/spritesPng'),
				)
				.map(value =>
					path.join(__dirname, `src/assets/images/sprites/spritesPng/${value}`),
				)
				.filter(value => !fs.statSync(value).isFile())
				.map(value => {
					const basename = path.basename(value);
					return new SpritesmithPlugin({
						src: {
							cwd: path.resolve(
								__dirname,
								`src/assets/images/sprites/spritesPng/${basename}`,
							),
							glob: '*.png',
						},
						apiOptions: {
							cssImageRef: `https://t1.kakaocdn.net/smartfactory/images/pc/sprite-${basename}.png`,
							spritesheet_info: {
								name: basename,
							},
						},
						target: {
							image: path.resolve(
								__dirname,
								`src/assets/images/sprites/sprite-${basename}.png`,
							),
							css: [
								[
									path.resolve(
										__dirname,
										`src/assets/scss/vendor/_sprite-${basename}.scss`,
									),
									{
										format: 'handlebars_based_template',
									},
								],
							],
						},
						customTemplates: {
							handlebars_based_template: path.resolve(
								__dirname,
								'src/assets/scss/vendor/spritesmith-mixins.handlebars',
							),
						},
					});
				}),
		],
	},
	chainWebpack: async () => {
		// https://vuetifyjs.com/en/styles/css-reset/#bootstrapping
		// vuetify global.sass, reset.scss 제거 (위 docs 참고 - 아래와같이 webpack 번들 실행되기 전에 내용 초기화방법밖엔 없는듯)
		const data = '';
		await fs.promises.writeFile(
			path.join(
				__dirname,
				'/node_modules/vuetify/src/styles/elements/_index.sass',
			),
			data,
		);
		await fs.promises.writeFile(
			path.join(
				__dirname,
				'/node_modules/vuetify/src/styles/generic/_index.scss',
			),
			data,
		);
	},
	css: {
		sourceMap: true,
		loaderOptions: {
			postcss: {
				plugins: [
					filterRules({
						filter: selector => {
							// const re = new RegExp(/^(html|body|\*|ul|ol|select|small)(\W|$)/, 'i')
							const exception = '.v-application';
							const exception1 = '.v-application--wrap';
							const exception2 = '.v-main';
							const exception3 = '.v-main__wrap';
							// return !re.test(selector) || !selector.includes(exception)
							return (
								!selector.includes(exception) &&
								!selector.includes(exception1) &&
								!selector.includes(exception2) &&
								!selector.includes(exception3)
							);
						},
						keepAtRules: true,
					}),
					autoprefixer,
				],
			},
		},
	},
	productionSourceMap: false,
	transpileDependencies: [ansiRegex, 'vuetify'],
	devServer: {
		https: true,
		proxy: {
			// proxy할 경로
			'/v1/': {
				// proxy되는 target
				target: process.env.PROXY_API,
				// cross origin 허용
				changeOrigin: true,

				// 중간에 바꾸고 싶은 문자가 있는경우.
				// 요청 /api/user/256 은 https://reqres.in/user/256 으로 변환
				// pathRewrite: {'^/api' : ''},
			},
			'/delivery/': {
				// proxy되는 target
				target: process.env.PROXY_API,
				// cross origin 허용
				changeOrigin: true,

				// 중간에 바꾸고 싶은 문자가 있는경우.
				// 요청 /api/user/256 은 https://reqres.in/user/256 으로 변환
				// pathRewrite: {'^/api' : ''},
			},
			'/common/': {
				// proxy되는 target
				target: process.env.PROXY_API,
				// cross origin 허용
				changeOrigin: true,

				// 중간에 바꾸고 싶은 문자가 있는경우.
				// 요청 /api/user/256 은 https://reqres.in/user/256 으로 변환
				// pathRewrite: {'^/api' : ''},
			},
			'/transit/': {
				// proxy되는 target
				target: process.env.PROXY_API,
				// cross origin 허용
				changeOrigin: true,

				// 중간에 바꾸고 싶은 문자가 있는경우.
				// 요청 /api/user/256 은 https://reqres.in/user/256 으로 변환
				// pathRewrite: {'^/api' : ''},
			},
			// "/transit/:id": {
			//   // proxy되는 target
			//   target: `${process.env.PROXY_API}/id`,
			//   // cross origin 허용
			//   changeOrigin: true
			//
			//   // 중간에 바꾸고 싶은 문자가 있는경우.
			//   // 요청 /api/user/256 은 https://reqres.in/user/256 으로 변환
			//   // pathRewrite: {'^/api' : ''},
			// }
		},
		disableHostCheck: true,
		// historyApiFallback: true
	},
};



6. 결과

31% 용량 감축