Vue.js 에서 권장하는 스타일 가이드. 코딩 컨벤션 같은 거랄까..



가이드 하는 내용 중 필수매우추천함 항목에 대해 설명한다.


1. 컴포넌트 이름에 합성어 사용

root 컴포넌트인 App 컴포넌트를 제외하고는 컴포넌트의 이름은 항상 "합성-어"를 사용해야 한다.

모든 HTML 엘리먼트의 이름은 한 단어로 표현되기 때문에, 충돌을 방지하려면 컴포넌트를 잘 설명할 수 있는 하이픈으로 구분된 합성어를 사용하는것이 좋다.

Bad

Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}

Good

Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}

2. 컴포넌트 데이터

컴포넌트 데이터는 함수여야 한다.
한 컴포넌트에서 data 프로퍼티를 사용할 때, 그 값은 무조건 한 객체를 리턴하는 함수여야 한다.

함수가 아닌 그냥 일반 객체일 경우, 컴포넌트를 두 영역에 렌더링 시 같은 data 객체를 참조하게 되어, 두 컴포넌트가 같은 값을 공유하게 되는 문제가 발생한다. (의도적인 거라면 할말없지만, mvvm 패턴을 따르지 않는 잘못된 방식이다.)

따라서 각 사용처마다 독립적인 data 객체를 가져야 하므로 꼭 함수로 정의하여 리터럴 객체를 리턴하도록 구현해야 한다.

Bad

Vue.component('some-comp', {
data: {
foo: 'bar'
}
})
export default {
data: {
foo: 'bar'
}
}

Good

Vue.component('some-comp', {
data: function () {
return {
foo: 'bar'
}
}
})
// In a .vue file
export default {
data () {
return {
foo: 'bar'
}
}
}
// It's OK to use an object directly in a root
// Vue instance, since only a single instance
// will ever exist.
new Vue({
data: {
foo: 'bar'
}
})


3. Props 정의

Prop 정의는 가능한 가장 자세히 기술해야 한다.
최소 type 이라도 기술하자.

Bad

// This is only OK when prototyping
props: ['status']

Good

props: {
status: String
}
// Even better!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}

4. v-for에 key 지정

v-for 사용시 항상 key를 지정해라.
내부 컴포넌트의 하위 트리의 상태를 유지하기 위해 key 가 필요하다. 

Vue.js 는 가상 돔 렌더링을 한다.
v-for 와 같은 디렉티브는 바인딩 된 배열에 따라 내용이 그려진다.
배열에 아이템이 하나라도 추가되면 전체 내용을 다시 그려야 하는 비싼 연산이 될 수 있다.
이련 경우를 방지하고자, 각 아이템의 key를 알려주면 정확한 비교가 가능하므로, 가상돔 업데이트 연산 과정이 명확하고 빠르게 수행되어 불필요한 업데이트를 방지 할 수 있다.

Bad

<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>

Good

<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>

5. v-if 와 v-for 를 동시에 사용하지 마세요

Vue가 디렉티브를 처리하는 과정에서, v-for 가 v-if 보다 우선순위가 더 높다.
따라서 아래와 같은 템플릿이
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
<li>
</ul>
이렇게 해석 될 수 있다.
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})

따라서 users 의 일부분만 렌더링 하려는 의도임에도, active user 가 변경되었는지 여부에 관계없이, 리렌더링 할때마다 전체 users 목록을 항상 순회해야 한다.
그 대신 아래와 같이 계산된 프로퍼티를 순회해라.
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
<li>
</ul>
이렇게 구현하면 아래와 같은 이점을 얻는다.
  • 필터링 된 리스트는 users 배열에 active 변경사항이 있는 경우만 다시 계산되므로 필터링이 훨씬 효과적이다.
  • v-for="users in activeUsers" 를 사용하면, 렌더링 동안에 active users 만 순회하므로 렌더링이 훨씬 효과적이다.
  • 로직과 화면 영역이 분리되어, 유지관리가 훨씬 수월 해 진다.

Bad

<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
<li>
</ul>
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
<li>
</ul>

Good

<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
<li>
</ul>
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
<li>
</ul>

6. 컴포넌트 스타일 스코프

최상단 App 컴포넌트와 layout 관련 컴포넌트의 스타일은 전역이 될 수도 있지만, 다른 모든 컴포넌트듸 스타일은 항상 scoped 여야 한다.

이거는 단순히 싱글 파일 컴포넌트에 대한 얘기이다. 항상 scoped 속성을 요구하는것이 아니다. 스코핑은 CSS module 을 통해 정의될 수도있고, BEM 과 같은 클래스-기반 방법도 있다.

컴포넌트 라이브러리들은  scoped 속성을 사용하는 대신 클래스-기반 전략을 취해야 한다.
클래스-기반 전략을 취하면 내부 스타일을 재정의 하는것이 더 쉬우며, 사람이 읽을 수 있는 클래스 이름을 사용하면(높은 특정도를 가지지는 않지만) 충돌을 일으키지는 않는다.

만약 다른 개발자들과, 외부 다른 서드파티 HTML/CSS 를 포함한 큰 프로젝트를 개발한다고 한다면, 일관된 스코핑은 스타일이 의도된 컴포넌트에만 적용되도록 확신할 수 있다.

scpoed 속성 외에도, 유니크한 클래스이름 사용은 서드파티와 충돌을 일으키지 않는데 도움을 받을 수 있다. 

예를들면 많은 프로젝트가 button, btn, icon 같은 클래스 이름을 사용하는데, 
굳이 BEM 스타일을 사용하지 않아도, 프로젝트나 및 컴포넌트 별로 ButtonClose-icon 같은 접두사를 추가하면 충돌을 방지할 수 있는 보호장치를 마련할 수 있다.

Bad

<template>
<button class="btn btn-close">X</button>
</template>

<style>
.btn-close {
background-color: red;
}
</style>

Good

<template>
<button class="button button-close">X</button>
</template>

<!-- Using the `scoped` attribute -->
<style scoped>
.button {
border: none;
border-radius: 2px;
}

.button-close {
background-color: red;
}
</style>
<template>
<button :class="[$style.button, $style.buttonClose]">X</button>
</template>

<!-- Using CSS modules -->
<style module>
.button {
border: none;
border-radius: 2px;
}

.buttonClose {
background-color: red;
}
</style>
<template>
<button class="c-Button c-Button--close">X</button>
</template>

<!-- Using the BEM convention -->
<style>
.c-Button {
border: none;
border-radius: 2px;
}

.c-Button--close {
background-color: red;
}
</style>


7. Private 속성 이름

plugin, mixin 등 을 정의할 때 private 속성은 항상 $_ 접두어를 사용하여 정의해라.

_ 접두어는 Vue 에서 private 속성들을 정의하는데 사용하므로, _ 접두어 사용은 vue 인스턴스의 속성을 오버라이딩 할 수 있는 위험성이 있다. 만약 그 속성명이 사용되지 않음을 확인했다 치더라도, 다음 버전에서 충돌이 일어나지 않을것이라는 보장이 없다.

$ 접두어는 Vue 에코시스템 안에서 사용자에게 노출시키려는 의미를 가진 특별한 속성이다. 따라서 $ 접두어 사용도 적절치 않다.

대신 두 접두어를 혼합한 $_ 접두어를 추천한다. 컨벤션으로 $_ 접두어는 사용자 정의 private 속성을 보장하여 Vue 와 충돌이 없다.


Bad

var myGreatMixin = {
// ...
methods: {
update: function () {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
_update: function () {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
$update: function () {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
$_update: function () {
// ...
}
}
}

Good

var myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update: function () {
// ...
}
}
}






서론

일반적으로 디스플레이는 1초에 화면을 60번 그린다. 

디스플레이가 1초에 60번 바뀌는데 웹페이지가 1초에 10번 그려진다면, 사용자는 페이지가 느리다고 느낀다. 우리는 이런 현상을 Jank 라고 부른다.

그렇다면 최적화가 필요한데, 웹페이지 렌더링을 최적화 한다는 것은 브라우저가 1초에 60번 렌더링 할 수 있도록 하면 된다.

웹페이지를 회작화 하기 위해 느린 부분을 찾아서 개선해야 한다. 어떤 부분이 느린지를 찾으려면 웹페이지가 화면을 어떻게 그리는지를 알아야 한다.




브라우저 렌더링이란?

사용자로부터 요청받은 내용을 브라우저 화면에 표시하는 일.

주로 크롬, 사파리, 파이어폭스, IE 브라우저가 있는데, 각 브라우저 마다 엔진이 다르므로, 용어나 렌더링 결과가 다르게 보이는 경우가 있다. (일명 브라우저 호환문제 라고 함)

브라우저 렌더링 엔진

Webkit (웹킷)

사피리와 크롬 브라우저가 사용중인 엔진이다. 최초 리눅스 플렛폼에서 동작하기 위해 제작된 오픈소스 엔진인데 애플이 맥과 윈도우에 사파리 브라우저를 지원하기 위해 수정을 가했다. http://webkit.org

Gecko (게코)

파이어폭스 브라우저가 사용중인 엔진 이름이다.


브라우저 렌더링 과정


brouser3



DOM트리는 HTML 구조를 있는 그대로 표현하는 트리라면
렌더트리는 스타일 정보를 더한, 즉 생상 또는 면적과 같은 시각적 속성이 있는 사갈형을 포함하고 있는데, 정해진 순서대로 화면서 표시되는 트리이다.
렌터트리 생성이 끝나면, 배치가 시작되는데 이것은 각 노드가 화면의 정확한 위치에 표시되는 것을 의미한다.


렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시하는데,
모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 시작한다. 네트워크로부터 나머지 내용이 전송되기를 기다리는 동시에 받은 내용의 일부를 먼저 화면에 표시한다.
brouser9

렌더링 엔진 용어

* layout(웹킷), reflow (게코): 브라우저가 요소를 화면에 배치하는 일.
* attachment(웹킷): 렌더트리를 생성하기위해 DOM 노드와 시각정보를 연결하는 과정.


스크립트와 스타일 시트의 진행 순서

스크립트

웹은 파싱과 실행이 동시에 수행되는 동기화(synchronous) 모델이다. 웹 페이지 제작자는 파서가 <script> 태그를 만나면 즉시 파싱하고 실행하기를 기대한다. HTML4에서는 스크립트가 실행되는 동안 문서의 파싱을 중단된다. 스크립트가 외부에 있는 경우 우선 네트워크로부터 자원을 가져와야 하는데 이 또한 실시간으로 처리되고 자원을 받을 때까지 파싱은 중단된다.
하지만 HTML5는 스크립트를 비동기(asynchronous)로 처리하는 속성을 추가했기 때문에, 웹 페이지 제작자는 스크립트를 "defer"로 표시해서 HTML 파싱을 중단하지 않을 수 있다. 문서 파싱이 완료된 이후에 스크립트가 실행된다 별도의 맥락에 의해 파싱되고 실행된다.

스타일 시트

이론적으로 스타일 시트는 DOM 트리를 변경하지 않기 때문에 문서 파싱을 기다리거나 중단 할 이유가 없다. 그러나 스크립트가 문서를 파싱하는동안 스타일 정보를 요청하는 경우라면 문제가 된다. 스타일이 파싱되지 않은 상태라면 스크립트는 잘못된 결과를 내 놓기 때문에 많은 문제를 야기한다. 

파이어폭스는 아직 로드중이거나 파싱중인 스타일 시트가 있는 경우 모든 스크립트 실행을 중단한다.
웹킷은 로드되지 않은 스타일 시트 가운데 문제가 될만한 속성이 있을 때만 스크립트 실행을 중단한다.



DOM 트리와 렌더 트리의 관계

렌더러는 DOM 요소에 부합하지만 1:1 로 대응하는 관계는 아니다. 예를들어 "head" 요소와 같은 비시각적 DOM 요소는 렌더 트리에 추가되지 않는다. 또한 display: none 도 마찬가지 이다.

brouser13






















Concepts

핵심은, webpack은 모던 JavaScript 어플리케이션의 정적 모듈 번들러라는 것이다. webpack 이 어플리케이션을 처리할 때, 내부적으로 모듈 디펜던시 그래프를 생성하여 하나 또는 그 이상의 번들을 생성한다.

4.0.0 버전 이후로, webpack 은 프로젝트를 빌드하기 위해 configuration file 이 필요 없다. 그럼에도 딱 맞는 설정을 할 수 있다.

webpack을 시작하기 위해서는, 핵심 컨셉을 이해할 필요가 있다.


  • Entry
  • Output
  • Loaders
  • Plugins
  • Mode
  • Browser Compatibility

이 문서는 high-level 오버뷰 이다. 


Entry

entry point는 모듈 webpack 이 내부적인 디펜던시 그래프를 만들기 시작하는 지점이다. webpack은 entry point 와 직접적, 간접적 디펜던시가 있는 다른 모듈과 라이브러리를 구분 할 것이다.

기본 값은 ./src/index.js 이지만, webpack configuration 에 entry 프로퍼티를 설정하여 다른 entry point 값(들)을 명시 할 수 있다.
예를 들면,

webpack.config.js

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

Output

output 프로퍼티는 webpack 에게 번들 파일들을 생성할 위치와 이름을 알려준다. 기본적으로 ./dist/main.js 이 주요 출력 파일이고 ./dist 폴더는 다른 파일들의 기본폴더이다.

설정으로 output 필드를 명시하여 설정할 수 있다.

webpack.config.js

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};


Loaders

webpack은 JavaScript 와 JSON 파일만 이해하는 반면, Loader는 다른 문법의 파일을 (예를들면, TypeScript, CoffeeScript ..) 유요한 JavaScript 파일로 변환하여 webpack이 디펜던시 그래프에 추가할 수 있도록 한다.


크게보면, loaderwebpack 구성에서 다음 두 가지 속성을 가진다.
  1. test 속성은 변환되어야하는 파일 또는 파일들을 나타낸다
  2. use 속성은 변환을 수행하는데 사용해야만 하는 loader 를 나타낸다.

webpack.config.js

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};


위의 설정에서 정의된 rules 속성은 두가지 프로퍼티가 요구된다. test 와 use

이것은 webpack의 컴파일러에게 다음과 같은 의미를 전달한다

헤이 webpack~

import require() 의 경로에서 .txt 로 리졸브되는 경로를 발견하면, 

번들에 추가하기 전에 raw-loader 를 사용하여 변환해줘


Plugins

loader 가 특정 타입의 모듈을 변환하는데 사용되는반면, 플러그인은 번들 최적화, 자원 관리, 환경변수 주입 등과 같은 좀 더 넓은 범위의 일들을 수행하는데 활용될 수 있다.

플러그인을 사용하기 위해서는, require()를 사용하여 로드하고 그것을 plugins 배열에 추가해야 한다. 
대부분의 플러그인들은 옵션을 통해 커스터마이징 가능하다. 서로다른 목적으로 하나의 플러그인을 여러번 사용할 수 있으므로, 항상 new 연산자로 플러그인을 호출하여 인스턴스를 생성해야 한다.


webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

module.exports = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

위의 예에서는, html-webpack-plugin 이 모든 생성되는 번들을 자동적으로 인젝팅하여 어플리케이션에 HTML 파일을 생성한다.


webpack은 많은 플러그인을 제공한다.

webpack 설정에서 플러그인의 사용은 간단하지만, 좀 더 유심히 봐야할 경우가 있다.  Learn more about them here.


Mode

mode 파라미터를 development, production 또는 none 으로 설정하면, 각 환경에 맞는 빌트인 된 webpack 최적화를 활성화 할 수 있다.
기본값은 production 이다.

Learn more about the mode configuration here and what optimizations take place on each value.


Browser Compatibility

webpack 은 IE8 이하를 제외한 모든 브라우저를 지원한다.
webpack import()와 requrie.ensure()를 위해 Promise가 필요하다. 만약 오래된 브라우저 지원을 원한다면, 저 표현식을 사용하기 전에 polyfill 을 로드해야 한다.


'WEB > Webpack' 카테고리의 다른 글

webpack 이란 무엇인가  (1) 2019.01.15

webpack

JavaScript 모듈화를 지원하기 위해 태어난 도구. 프레임웤.

webpack 은 모듈 시스템 지원 외에도, 로더, 빠른 컴파일 속도 등 장점이 많다.

Node.js 가 설치된 환경에서 실행되는데, 사용법은 아래와 같다.


npm install webpack -g


webpack이 설치되면 엔트리 파일 및 번들 파일 형식으로 명령어를 실행하여 모듈을 컴파일 한다.

webpack ./entry.js bundle.js


webpack 은 컴파일 시 엔트리 파일을 시작으로, 디펜던시가 있는 다른 파일들을 엮어서 하나의 번들 파일을 만든다.

이 묶여진 번들 파일을 HTML 에서 로딩하기만 하면 전체 모듈을 사용할 수 있다.


그림 1 컴파일 과정


엔트리 파일이 여러개 일 경우는 엔트리 파일마다 번들 파일이 생성된다.

그림 2 엔트리 파일이 여러 개일 때의 컴파일 과정


컴파일 시 --watch 옵션을 사용하면, 모듈이 변경됨을 감지하여 자동으로 컴파일 한다.

webpack --watch ./entry.js bundle.js


webpack config

webpack 을 사용하여 컴파일 할 때 명령어 옵션이 많거나 입력할 내용이 많으면 매우 불편하다.
따라서 이런 입력 내용들을 설정 파일을 만들어 관리 할 수 있다.

module.exports = { context: __dirname + '/app', // 모듈 파일 폴더 entry: { // 엔트리 파일 목록 app: './app.js' }, output: { path: __dirname + '/dist', // 번들 파일 폴더 filename: '[name].bundle.js' // 번들 파일 이름 규칙
}

이런 형태로 webpack.config.js 파일을 작성하고 아래와 같이 명령어만 입력하면 컴파일이 된다.

webpack

로더

webpack 은 다양한 로더 라이브러리를 지원한다.

예를들면 less 로더는 .less 파일을 순수 스타일 파일인 .css 로 로딩할 수 있도록 도와준다.
vue 로더는 .vue 파일을 순수 HTML + JavaScript + CSS 파일로 로딩할 수 있도록 도와준다.

ECMAScript 2015 를 사용할 수 있게 컴파일 하는 Babel 도 사용할 수 있다.





참고


'WEB > Webpack' 카테고리의 다른 글

webpack 주요 컨셉 알아보기  (0) 2019.01.16

HTML 문서에서 공백은 spacebar를 아무리 눌러도 삽입되지 않는다.

 이때는 &nbsp  태그를 삽입하여 공백을 넣어준다.



예제

공백을 &nbsp;&nbsp;&nbsp; 삽입할 경우에는 &nbsp;&nbsp;&nbsp;&nbsp; nbsp 태그를 이용하여 &nbsp;&nbsp;&nbsp; 연속적으로 삽입합니다.

공백을     삽입할 경우에는      nbsp 태그를 이용하여     연속적으로 삽입합니다.


'WEB > HTML' 카테고리의 다른 글

날짜 animation  (0) 2014.11.03
page loading 시 scrolling  (0) 2012.08.19
HTML 태그 총정리  (0) 2012.08.15
HTML 페이지 프레임 나누기  (0) 2012.08.14
HTML에 웹폰트 적용하기  (0) 2012.08.12
    
    

      $('#cel').text(Math.random()*1000);
      $('#cel').each(function() {
        var $this = $(this);
        $({Counter:0}).animate({Counter:$this.text()}, {
          duration: 5000,
          easing: 'easeOutExpo',
          step: function() {
            $this.text(Math.ceil(this.Counter));
          }
        });
      });

'WEB > HTML' 카테고리의 다른 글

HTML 페이지에 공백(space) 삽입  (0) 2015.01.01
page loading 시 scrolling  (0) 2012.08.19
HTML 태그 총정리  (0) 2012.08.15
HTML 페이지 프레임 나누기  (0) 2012.08.14
HTML에 웹폰트 적용하기  (0) 2012.08.12



window.scrollTo(0, window.innerHeight + this.getEndPositionOfChar().y);
 // 두번째 인자는 offsetLeft 의 값



'WEB > HTML' 카테고리의 다른 글

HTML 페이지에 공백(space) 삽입  (0) 2015.01.01
날짜 animation  (0) 2014.11.03
HTML 태그 총정리  (0) 2012.08.15
HTML 페이지 프레임 나누기  (0) 2012.08.14
HTML에 웹폰트 적용하기  (0) 2012.08.12



참고할만한 블로그.


http://ektha307.com.ne.kr/tags/tag_abc01.html

'WEB > HTML' 카테고리의 다른 글

HTML 페이지에 공백(space) 삽입  (0) 2015.01.01
날짜 animation  (0) 2014.11.03
page loading 시 scrolling  (0) 2012.08.19
HTML 페이지 프레임 나누기  (0) 2012.08.14
HTML에 웹폰트 적용하기  (0) 2012.08.12

+ Recent posts