AngularJS開発でフォルダ構成をどうするか悩んだのでまとめてみました。
さんざん語られている話題ではありますが。。
#部品単位
こちらなどで紹介いただいている
yeomanを使用するとこんな感じ。
│ bower.json
│ Gruntfile.js
│ package.json
│
├─app
│ │ 404.html
│ │ bower.json
│ │ favicon.ico
│ │ index.html
│ │ robots.txt
│ ├─bower_components
│ ├─images
│ ├─scripts
│ │ │ app.js
│ │ │
│ │ └─controllers
│ │ about.js
│ │ main.js
│ │
│ ├─styles
│ │ main.css
│ │
│ └─views
│ about.html
│ main.html
…
コントローラやテンプレートと部品ごとに分割しています。
スッキリしているし、フォルダの中が同じ部品だとわかっているので、後々置換などが発生しても楽そうです。
#ユースケース単位
Mastering Web Application Development with AngularJS(Kindle安い!2014/11/5で1,801円)で紹介されているパターンだとこんな感じ。ちょっと長めに引用。
ソースはこちらです。
├─client
│ │ .gitignore
│ │ gruntFile.js
│ │ package.json
│ │
│ ├─dist
│ ├─src
│ │ │ index.html
│ │ │
│ │ ├─app
│ │ │ │ app.js
│ │ │ │ header.tpl.html
│ │ │ │ notifications.tpl.html
│ │ │ │
│ │ │ ├─admin
│ │ │ │ │ admin.js
│ │ │ │ │
│ │ │ │ ├─projects
│ │ │ │ │ admin-projects.js
│ │ │ │ │ projects-edit.tpl.html
│ │ │ │ │ projects-list.tpl.html
│ │ │ │ │
│ │ │ │ └─users
│ │ │ │ admin-users-edit.js
│ │ │ │ admin-users-list.js
│ │ │ │ admin-users.js
│ │ │ │ uniqueEmail.js
│ │ │ │ users-edit.tpl.html
│ │ │ │ users-list.tpl.html
│ │ │ │ validateEquals.js
│ │ │ │
│ │ │ ├─dashboard
│ │ │ │ dashboard.js
│ │ │ │ dashboard.tpl.html
│ │ │ │
│ │ │ ├─projects
│ │ │ │ │ projects-list.tpl.html
│ │ │ │ │ projects.js
│ │ │ │ │
│ │ │ │ ├─productbacklog
│ │ │ │ │ productbacklog-edit.tpl.html
│ │ │ │ │ productbacklog-list.tpl.html
│ │ │ │ │ productbacklog.js
│ │ │ │ │
│ │ │ │ └─sprints
│ │ │ │ │ sprints-edit.tpl.html
│ │ │ │ │ sprints-list.tpl.html
│ │ │ │ │ sprints.js
│ │ │ │ │
│ │ │ │ └─tasks
│ │ │ │ tasks-edit.tpl.html
│ │ │ │ tasks-list.tpl.html
│ │ │ │ tasks.js
│ │ │ │
│ │ │ └─projectsinfo
│ │ │ list.tpl.html
│ │ │ projectsinfo.js
│ │ │
│ │ ├─assets
│ │ │ │ favicon.ico
│ │ │ │
│ │ │ └─img
│ │ │ glyphicons-halflings-white.png
│ │ │ glyphicons-halflings.png
│ │ │ spinner.gif
│ │ │
│ │ ├─common
│ │ │ ├─directives
│ │ │ │ │ gravatar.js
│ │ │ │ │ modal.js
│ │ │ │ │
│ │ │ │ └─crud
│ │ │ │ crud.js
│ │ │ │ crudButtons.js
│ │ │ │ edit.js
│ │ │ │
│ │ │ ├─resources
│ │ │ │ backlog.js
│ │ │ │ projects.js
│ │ │ │ sprints.js
│ │ │ │ tasks.js
│ │ │ │ users.js
│ │ │ │
│ │ │ ├─security
│ │ │ │ │ authorization.js
│ │ │ │ │ index.js
│ │ │ │ │ interceptor.js
│ │ │ │ │ retryQueue.js
│ │ │ │ │ security.js
│ │ │ │ │
│ │ │ │ └─login
│ │ │ │ form.tpl.html
│ │ │ │ login.js
│ │ │ │ LoginFormController.js
│ │ │ │ toolbar.js
│ │ │ │ toolbar.tpl.html
│ │ │ │
│ │ │ └─services
│ │ │ breadcrumbs.js
│ │ │ crud.js
│ │ │ crudRouteProvider.js
│ │ │ exceptionHandler.js
│ │ │ httpRequestTracker.js
│ │ │ i18nNotifications.js
│ │ │ localizedMessages.js
│ │ │ notifications.js
│ │ │
│ │ └─less
│ │ stylesheet.less
│ │ variables.less
コントローラとテンプレートが同一フォルダで、サービスやディレクティブは共通部品として配置されています。
確かに、コントローラとテンプレートが一緒のほうが開発しやすいですね。
上記書籍にはこの構成について、結構なページ数の解説があって、これだけでも良心的だなあと思います。
上記構成でのgruntoの抜粋を以下に。
module.exports = function (grunt) {
~略~
// Project configuration.
grunt.initConfig({
distdir: 'dist',
pkg: grunt.file.readJSON('package.json'),
banner:
'/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
src: {
js: ['src/**/*.js'],
jsTpl: ['<%= distdir %>/templates/**/*.js'],
specs: ['test/**/*.spec.js'],
scenarios: ['test/**/*.scenario.js'],
html: ['src/index.html'],
tpl: {
app: ['src/app/**/*.tpl.html'],
common: ['src/common/**/*.tpl.html']
},
less: ['src/less/stylesheet.less'], // recess:build doesn't accept ** in its file patterns
lessWatch: ['src/less/**/*.less']
},
~略~
html2js: {
app: {
options: {
base: 'src/app'
},
src: ['<%= src.tpl.app %>'],
dest: '<%= distdir %>/templates/app.js',
module: 'templates.app'
},
common: {
options: {
base: 'src/common'
},
src: ['<%= src.tpl.common %>'],
dest: '<%= distdir %>/templates/common.js',
module: 'templates.common'
}
},
concat:{
dist:{
options: {
banner: "<%= banner %>"
},
src:['<%= src.js %>', '<%= src.jsTpl %>'],
dest:'<%= distdir %>/<%= pkg.name %>.js'
},
index: {
src: ['src/index.html'],
dest: '<%= distdir %>/index.html',
options: {
process: true
}
},
~略~
},
~略~
html2jsで「src/app/**/*.tpl.html」、「src/common/**/*.tpl.html」配下のhtmlをJS化、
モジュール単位のJSファイルをconcatで「<%= pkg.name %>.js」にまとめています。
AngularJSで書かれている分にはファイル結合の順序は問われないと。
index.htmlのビルド前後の差分は以下。
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<link rel="stylesheet" type="text/css" href="/static/<%= grunt.config.get('pkg.name') %>.css"/>
<script type="text/javascript" src="/static/jquery.js"></script>
<script type="text/javascript" src="/static/angular.js"></script>
<script type="text/javascript" src="/static/mongolab.js"></script>
<script type="text/javascript" src="/static/bootstrap.js"></script>
<script type="text/javascript" src="/static/<%= grunt.config.get('pkg.name') %>.js"></script>
</head>
~略~
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<link rel="stylesheet" type="text/css" href=/angular-app.css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="mongolab.js"></script>
<script type="text/javascript" src="bootstrap.js"></script>
<script type="text/javascript" src="angular-app.js"></script>
</head>
~略~
どちらもよいですが、個人的にはユースケース単位で分割したほうが好みでした。