+

300 000 км/с

много данных - мало памяти

требования к приложению

мало времени

поддержка

Метеор

 

платформа

клиент

сервер

пакеты

сборка

cmd-утилита

*.meteor.com, Galaxy

 

Что нужно понимать:

PubSub-паттерн

Реакт

 

библиотека

клиент

никаких шабл***ов

веб-компоненты

Фейcбук

 

 

 

 

Проектирование UI

/* app.jsx */

var HelloUser = React.createClass({
  mixins: [ReactMeteorData],
  getMeteorData() {
    return {
      currentUser: Meteor.user()
    };
  },
  render() {
    return <span>
            Hello {this.data.currentUser.username}!
        </span>;
  }
});


if (Meteor.isClient) {
  Meteor.startup(function () {
    ReactDOM.render(<HelloUser />, 
        document.getElementById("app"));
  });
}

curl https://install.meteor.com/ | sh
meteor create project-name
cd project-name 
meteor add react
meteor add react-router
meteor run

Hello, MeteoReact

/* app.html */

<head>
    <title>JSNN</title>
</head>

<body>
<div id="app"></div>
</body>

izzilab:material-ui

qnub:accounts-react-material-ui

Зачем Метеору - Реакт?

/* MessageList.jsx */
MessageList = React.createClass({
  mixins: [ReactMeteorData],

  getMeteorData() {
    return {
      messages: Messages.find({}).fetch()
    }
  },
  
  renderMessages() {
    return this.data.messages.map((message) => {
      return <Message key={message._id} message={message} />;
    });
  },

  handleSubmit(event) {
    event.preventDefault();
    let message = ReactDOM.findDOMNode(this.refs.textInput).value;
    Messages.insert({text: message, user: Meteor.user()});
    ReactDOM.findDOMNode(this.refs.textInput).value = "";
  },

  renderForm() {
    return <form onSubmit={this.handleSubmit} >
      <input
        type="text"
        ref="textInput"
        name="message"
        placeholder="Enter message..." />
    </form>
  },

  render() {
    return (
      <div className="container">
        <header><h2>Messages</h2></header>
        {this.renderForm()}
        <ul>{this.renderMessages()}</ul>
      </div>
    );
  }
});
<head>
    <title>JSNN</title>
</head>

<body>
<div id="app">{{>Messages}}</div>
</body>

<template name='Messages'>
    <div className="container">
        <header><h2>Messages</h2></header>
        <form>
            <input type="text" ref="textInput" name="message" placeholder="Enter message..." />
        </form>
        <ul>
            {{#each messages}}
                <li>{{formatedTime}} - {{text}}</li>
            {{/each}}
        </ul>
    </div>
</template>

JSX

HTML

Template.Messages.rendered = function () {
    this.subscribe('messages');
};

Template.Messages.events({
    'submit form': function (event, template) {
        event.preventDefault();
        var message = template.$('input').val();
        Messages.insert({text: message, user: Meteor.user()});
        template.$('input').val('');
    }
});

Template.Messages.helpers({
    messages: function () {
        return Messages.find();
    },
    formatedTime: function () {
        return  moment(this.time).format('hh:mm:ss');
    }
});

JS

Blaze

React

Зачем Реакту - Метеор?

* Jean-Jacques Dubray "Why I No Longer Use MVC Frameworks"

(https://habrahabr.ru/post/277113/)

/* Message.jsx */

Message = React.createClass({
  propTypes: {
    message: React.PropTypes.object.isRequired
  },
  
  formatTime(time) {
    return moment(time).format('h:mm A');
  },
  
  render() {
    return (
      <li>{this.formatTime(this.props.message.time)} - {this.props.message.text}</li>
    );
  }
});



/* MessageList.jsx */

MessageList = React.createClass({
  mixins: [ReactMeteorData],

  getMeteorData() {
    return {
      messages: Messages.find({}).fetch()
    }
  },
  
  renderMessages() {
    return this.data.messages.map((message) => {
      return <Message key={message._id} message={message} />;
    });
  },

  handleSubmit(event) {
    event.preventDefault();
    
    let message = ReactDOM.findDOMNode(this.refs.textInput).value.trim();

    Meteor.call("addMessage", message);

    ReactDOM.findDOMNode(this.refs.textInput).value = "";
  },

  renderForm() {
    return <form onSubmit={this.handleSubmit} >
      <input
        type="text"
        ref="textInput"
        name="message"
        placeholder="Enter message..." />
    </form>
  },

  render() {
    return (
      <div className="container">
        <header>
          <h2>Messages</h2>
        </header>
        
        {this.renderForm()}

        <ul>
          {this.renderMessages()}
        </ul>
      </div>
    );
  }
});



/*demo.html*/

<head>
  <title>demo</title>
</head>

<body>
  <h1>Welcome to Meteor + React!</h1>

  <div id="render-target"></div>
</body>




/*demo.jsx*/

Messages = new Mongo.Collection("Messages", {});

Meteor.methods({
  addMessage(text) {
    let message = {
      time: new Date(),
      text: text
    };

    Messages.insert(message);
  }
});


if (Meteor.isClient) {
  Meteor.startup(function () {
    ReactDOM.render(<MessageList />, document.getElementById("render-target"));
  });
}
body {
  width: 500px;
  margin: 0 auto;
  font-family: sans-serif;
  color: #333;
  font-weight: 100;
}

nav {
  margin-top: 10px;
  background: #f6f8f9; /* Old browsers */
  background: -moz-linear-gradient(top, #f6f8f9 0%, #e5ebee 50%, #d7dee3 51%, #f5f7f9 100%); /* FF3.6+ */
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f6f8f9), color-stop(50%,#e5ebee), color-stop(51%,#d7dee3), color-stop(100%,#f5f7f9)); /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(top, #f6f8f9 0%,#e5ebee 50%,#d7dee3 51%,#f5f7f9 100%); /* Chrome10+,Safari5.1+ */
  background: -o-linear-gradient(top, #f6f8f9 0%,#e5ebee 50%,#d7dee3 51%,#f5f7f9 100%); /* Opera 11.10+ */
  background: -ms-linear-gradient(top, #f6f8f9 0%,#e5ebee 50%,#d7dee3 51%,#f5f7f9 100%); /* IE10+ */
  background: linear-gradient(to bottom, #f6f8f9 0%,#e5ebee 50%,#d7dee3 51%,#f5f7f9 100%); /* W3C */
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f6f8f9', endColorstr='#f5f7f9',GradientType=0 ); /* IE6-9 */
  padding: 10px;
  border-radius: 4px;
  border: 1px solid #abe;
}

nav a {
  text-decoration: none;
  font-family: sans-serif;
  color: #567;
  font-weight: 400;
  font-size: 14px;
}

nav a:hover {
  color: #444;
}

#login-buttons {
  font-weight: 400;
}
#login-buttons a {
  text-decoration: none;
}

h2 {
  border-bottom: 1px solid #abe;
  font-family: sans-serif;
  font-weight: 100;
}

input[name="message"] {
  width: 81%;
  padding: 7px 10px;
  border-radius: 3px;
  border: 1px solid #999;
  box-shadow: inset 0px 1px 2px 0px #ccc;
}

input[value="Send"],
input[value="Delete"],
input[value="Follow"],
input[value="Unfollow"] {
  padding: 6px 19px 7px 19px;
  color: white;
  border-radius: 3px;
  cursor: pointer;
}

input[value="Send"] {
  border: 1px solid #3498db;
  background-color: #3498db;
}

input[value="Delete"] {
  background-color: #ecf0f1;
  border: 1px solid #bdc3c7;
  color: #7f8c8d;
  text-shadow: 0px 1px 0px #fff;
  float: right;
  margin-top: -17px;
}

input[value="Delete"] {
  background-color: #ecf0f1;
  border: 1px solid #bdc3c7;
  color: #7f8c8d;
  text-shadow: 0px 1px 0px #fff;
}

input[value="Follow"] {
  background-color: #2ecc71;
  border: 1px solid #27ae60;
}
input[value="Unfollow"] {
  background-color: #f39c12;
  border: 1px solid #e67e22;
}

input[name="username-search"] {
  margin-top: 10px;
  width: 477px;
  padding: 10px 10px;
  border: 1px solid #999;
  border-radius: 3px;
  font-size: 16px;
  box-shadow: inset 0px 1px 3px 0px #ccc;
}

.countdown {
  font-weight: bold;
}

ul {
  list-style-type: none;
  padding-left: 0px;
}

ul li {
  min-height: 38px;
  line-height: 30px;
}

ul li input[value="Delete"] {
  margin-top: 0px;
}

ul li a {
  font-weight: bold;
  text-decoration: none;
  color: #2980b9;
}

h2 a {
  font-weight: bold;
  text-decoration: none;
  color: #333;
}

#follow input, #unfollow input {
  width: 100%;
  margin-bottom: 20px;
  font-size: 16px;
}

Код+разметка
110 строк

Стили
141 строка

Зачем мне Реакт?

Зачем мне Метеор?

 

Реакт

https://facebook.github.io/react/docs/getting-started.html

 

Метеор

http://ru.discovermeteor.com/

 

Метеор+Реакт

https://www.meteor.com/tutorials/react/

http://react-in-meteor.readthedocs.org/en/latest/

Meteor

Messages = new Mongo.Collection('messages');

if (Meteor.isServer) {
    Meteor.publish('messages', () => {
        return Messages.find();
    });
}

if (Meteor.isClient) {
    Meteor.subscribe('messages');
}

Магия клиент-серверного взаимодействия

Консоль брауZера

Messages.insert({text: 'Hello, guys!'})      // Добавление записи

Messages.find().fetch()       // Все записи (автоматическая синхронизация с сервером)

Message = React.createClass({
  propTypes: {
    message: React.PropTypes.object.isRequired
  },
  
  formatTime(time) {
    return 
        moment(this.props.message.time)
        .format('h:mm A');
  },
  
  render() {
    return (
      <li>{this.formatTime()}
         - 
        {this.props.message.text}</li>
    );
  }
});
Message = React.createClass({
  displayName: 'Message',

  propTypes: {
    message: React.PropTypes.object.isRequired
  },

  formatTime: function formatTime(time) {
    return moment(time).format('h:mm A');
  },
  render: function render() {
    return React.createElement(
      'li',
      null,
      this.formatTime(this.props.message.time),
      ' - ',
      this.props.message.text
    );
  }
});

React

JSX

чистый JS

Meteor+React

By lawrentiy

Meteor+React

Презентация для разработчиков, кто знает Метеор, но кто хочет использовать React. Или для тех, кто знает React, умеет разрабатывать frontend-приложения и кто хочет писать для него backend-часть.

  • 1,555