Meteor.jsとは
node.jsのフルスタックフレームワーク。なにかプロトタイプを作ろうと思った時にさくっと作ることができます。リアルタイムwebアプリが作れで、リアクティブにデータが反映されます。
これは、ページをリロードしなくても、データの更新があれば該当の値が更新されるものです。チャットアプリのような挙動です。それがチョー簡単につくれます。
React.jsとは
FacebookがOSSとして公開していて、実際にプロダクトにも使用しているフロントエンドのフレームワークです。スタートアップ界隈でもよく使われています。このReactの概念さえ覚えてしまえば、React Nativeをつかってネイティブアプリの構築もすることができます。
Meteor インストール (MAC)
これをターミナルで叩く
curl https://install.meteor.com/ | sh
Meteor プロジェクト作成
プロジェクトを起きたいディレクトリまで移動して、
meteor create "アプリ名"で作成
meteor create sample-meteor-react
Meteor 起動
プロジェクトのディレクトリに移動して、”meteor”と打てばサーバー起動
cd sample-meteor-react
meteor
http://localhost:3000/を開いて、”Welcome to Meteor!”の画面が表示されていれば成功
React インストール
meteor npm install --save react react-dom
セキュリティ上問題のある2つのパッケージを削除
meteorにはデフォルトでinsecure
とautopublish
というパッケージが入っています。insecureはサイトに訪れたすべての人がDBを操作できるように初期設定されているもので、autopublishもサイトに訪れた人がすべての人がすべてのデータを見れるようにしているものです。
本番環境ではデータの閲覧、操作権限誰にでもできないようにサーバーサイドでチェックを入れるのですが、meteorは公開前のプロトタイピングがしやすいように、このような権限を全開放する初期設定になっています。
いずれ消すので、とっととその2つを消し去りましょう。
meteor remove insecure
meteor remove autopublish
既存のテンプレートを削除
meteorにはデフォルトでblazeというテンプレートが入っています。今回はReactを使用するのでこちらを削除します。
meteor remove blaze-html-templates
meteor add static-html
React用にMeteorのファイルを変更
main.htmlを変更
id="render-target"の中にreactの要素が流し込まれます。
<head>
<title>sample-meteor-react</title>
</head>
<body>
<div id="render-target"></div>
</body>
main.jsを変更
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import RouteApp from '../imports/router/RouteApp.jsx';
Meteor.startup(() => {
render(<RouteApp />, document.getElementById('render-target'));
});
ここのRouteAppというのは後で作る、ルーティング用のReactコンポネントです。今の段階でページにアクセスするとエラーが出るのですが、気にせんといて次に進んでください。
React用のディレクトリ作成
importsフォルダを作成、中身はこのような感じ
imports/
├ router
├ ui
└ components
└ pages
react-router-domインストール
https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom
npm install --save react-router-dom
react routerを設定
router/RouteAppを編集
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch,
} from 'react-router-dom';
import Top from '../ui/pages/Top.jsx';
import AnotherPage from '../ui/pages/AnotherPage.jsx';
export default class RouteApp extends Component{
render(){
return(
<Router>
<Switch>
<Route exact path="/" component={Top}/>
<Route exact path="/another" component={AnotherPage}/>
</Switch>
</Router>
)
}
}
これでルーティングの設定は完了です。
他のページを追加したいときも<Switch></Switch>
の中に
<Route exact path="パス" component={表示するコンポネント}/>
の形で追加していけばよいです。シンプルですね。
Reactコンポネント作成
imports/ui/pagesの中に
- Top.jsx
- AnotherPage.jsx
を作成します。
import React, {Component} from "react";
export default class Top extends Component{
render(){
return(
<div>topページ</div>
)
}
}
import React, {Component} from "react";
export default class AnotherPage extends Component{
render(){
return(
<div>違うページ</div>
)
}
}
ページ確認
これで、
http://localhost:3000/
http://localhost:3000/another
にアクセスして、さっき作ったページが表示されていればOKです!
ページ遷移はこれで完璧です。
Meteorでのデータの扱い
ここからは、meteorでのデータの扱うために、サンプルでto doアプリを作成します。
collectionの作成
collectionとは、SQLのテーブルのようなものです。
- imports/apiフォルダを作成
- apiフォルダの中にcollections.jsを作成
import {Mongo} from 'meteor/mongo'
export const Todos = new Mongo.Collection('todo');
これをserver/main.jsにてimport
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';
Meteor.startup(() => {
// code to run on server at startup
});
Methodの作成
collectionが作成できたので、次はメソッドの作成です。
メソッドとは、実査にDBへの処理記述するもので、MVCでいうモデルに該当します。
- imports/api/にmethods.jsを作成
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { Todos } from './collections.js';
Meteor.methods({
'addTodos'(){
todo = {
date:new Date(),
content:"test!"
};
Todos.insert(todo);
},
})
ここでは、todoリストにデータを挿入するメソッドを作成しました。
そして、コレクションのときと同じようにserver/main.jsにてimport
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';
import '../imports/api/methods.js';
Meteor.startup(() => {
// code to run on server at startup
});
メソッドを実行する方法
Top.jsxを以下のように編集します
import React, {Component} from "react";
export default class Top extends Component{
addTodo(){
Meteor.call('addTodo')
}
render(){
return(
<div>
<div>topページ</div>
<a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
</div>
)
}
}
これで、to do リストに追加をクリックすれば、データが追加できるようになりました。
データの表示
Meteorはリアクティブなフレームワークです。ですが、全ての情報をクライアント側に送るわけにはいかないので、どの情報を送るかどうかをこちらで管理しなければいけません。それを制御するために、以下のファイルを作成します。
import { Meteor } from 'meteor/meteor';
import { Todos } from '../collections.js';
if(Meteor.isServer){
Meteor.publish('getTodos', function(){
return Todos.find({});
});
}
今回はすべてのtodoリストをクライアント側に返すものを作成しました。
返したいものをreturnするだけです。
また、これもserver/main.jsにimportします
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';
import '../imports/api/methods.js';
import '../imports/api/server/publications.js';
Meteor.startup(() => {
// code to run on server at startup
});
to doリストの表示
MeteorのデータをReactに流し込むためには、withTrackerを使えるようにする必要がります。そのためにreact-meteor-dataをインストール
meteor add react-meteor-data
今回はtopページに表示するので、Top.jsxを以下のように編集
import React, {Component} from "react";
import { withTracker } from 'meteor/react-meteor-data';
import {Todos} from '../../api/collections.js';
class Top extends Component{
addTodo(){
Meteor.call('addTodo')
}
renderTodos(){
let todos = this.props.todos;
if(todos){
return todos.map((todo,index)=>{
return(
<li key={index}>
{todo.content}
</li>
);
});
}
}
render(){
return(
<div>
<div>topページ</div>
<a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
<ul>
{this.renderTodos()}
</ul>
</div>
)
}
}
export default withTracker(()=>{
let todos;
if(Meteor.subscribe('getTodos').ready()){
todos = Todos.find().fetch();
}
return{
todos:todos
}
})(Top);
withTrackerでreturnしたデータはpropsとして参照することができます。
先程、定義したものを
Meteor.publish('getTodos', function(){
return Todos.find({});
});
このように呼び出すことができます。
Meteor.subscribe('getTodos')
また、ready()をつけることによって、データのsubscribeの準備ができたかどうかを確認しています。react + meteorの組み合わせだと、このサブスクライブの準備ができるまでに一回renderされるので、チェックが必要です。
これで、”to do リストに追加”をクリックする度にどんどん”test!”という項目が追加されていっているかと思います。
Methodに引数を渡して、入力した値をDBに格納
このままだと、同じ文章が並ぶので、入力欄を設けて、メソッドに引数を渡して処理をする方法がこちらです。
まずはformを作成し、送信できるようにTop.jsxを修正
import React, {Component} from "react";
import { withTracker } from 'meteor/react-meteor-data';
import {Todos} from '../../api/collections.js';
class Top extends Component{
constructor(props){
super(props);
this.state = {
todoText:"",
}
}
addTodo(){
Meteor.call('addTodo')
}
renderTodos(){
let todos = this.props.todos;
if(todos){
return todos.map((todo,index)=>{
return(
<li key={index}>
{todo.content}
</li>
);
});
}
}
submitForm(e){
e.preventDefault();
let todoText = this.state.todoText;
Meteor.call('addTodo',todoText);
}
render(){
return(
<div>
<div>topページ</div>
<form onSubmit={(e)=>this.submitForm(e)}>
<input type="text" onChange={(e)=>this.setState({todoText:e.target.value})} value={this.state.todoText}/>
<input type="submit" value="リストに追加"/>
</form>
<a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
<ul>
{this.renderTodos()}
</ul>
</div>
)
}
}
export default withTracker(()=>{
let todos;
if(Meteor.subscribe('getTodos').ready()){
todos = Todos.find().fetch();
}
return{
todos:todos
}
})(Top);
次に、methods.jsも変更
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { Todos } from './collections.js';
Meteor.methods({
'addTodo'(content){
var todo = {
date:new Date(),
content:content
};
Todos.insert(todo);
},
})
メソッドは第二引数以降で、引数を渡せます。
Meteor.call('メソッド名',args...)
また、Meteor.subscribeも第二引数以降で引数を渡せます。
Meteor.subscription('パブリッシュ名',args...)
完成
これで自由に値の入力ができるようになりました。
todoリストとしては編集も削除もできず、すべてのユーザに同じものが表示されるゴミですが、
reactでイベント作る→メソッドを作成してそれを呼び出す→メソッド内でmongoDBを操作
で同じようにDBの処理ができ、
表示したいデータをpublication.jsで定義→それを使用するページでMeteor.subscribe('publications.js定義した関数名')で呼び出し
でデータの受け取りを行うことができます。
例えば、指定したuserIdのtodoしかか表示しない場合は、Meteor.subscribeにuser引数を渡して、
Meteor.publish('getTodos', function(userId){
return Todos.find({userId:userId});
});
のようにすればOkです。
まとめ
meteorの解説中心となってしまいました。Reactは他の方々がわかりやすい説明をしているので、ググってそちらを見ていただくほうが良いですな。
React + meteor + react routerの組み合わせはこのようにサクッとプロダクトを作れるので、まだまだ駆け出しで、jsしか知らない人がさくっと高速開発するのにはおすすめです。(ただ、デプロイちょっと面倒です。今度書きます。)
あとは、使う場面があれば、リアルタイムDBもすごく魅力的。
今回のコードはこちらです。
https://github.com/keitaisozaki/sample-meteor-react
日本はあまり使っている人に出会ったことがないのですが、使う人増えたら嬉しいです