FSM 4: Давайте создадим нашу первую страницу!

Добро пожаловать в Часть 4 руководства Fresh Start Meteor, которое поможет вам создать новый проект Meteor с использованием ES6, React, Redux, FlowRouter и Sass. Чувствовать себя потерянным? Начните с Часть 1.

Отлично, мы установили Meteor и наши пакеты и разобрались с организацией нашего каталога. Но если вы запустите свое приложение и посетите localhost:3000, вы увидите пустую страницу и ошибку консоли.

Эта ошибка связана с тем, что микроприложение по умолчанию, включенное в новую установку Meteor, было создано с использованием Blaze, которое мы удалили из нашего проекта! Я знаю, что сказал, что не буду заставлять вас следовать какому-то конкретному примеру, но чтобы представить структуру каталогов, которую мы построили в прошлой статье, практикуя React и ES6, давайте все равно воспроизведем это микроприложение вместе. Как только мы это рассмотрим, мы будем работать с более общими примерами.

Когда вы просматриваете свой проект прямо сейчас, вы заметите четыре файла: client/main.html, client/main.js, client/main.css. и server/main.js. Только первые два содержат какой-либо код.

С ES6 мы не зависим от порядка загрузки файлов Meteor по умолчанию, потому что вместо этого мы импортируем каждый файл по мере необходимости. Нашей первой задачей будет разбить client/main.html на макет, страницу и два компонента, добавить соответствующую логику из client/main.js, установить создать маршрут и импортировать все в корневой каталог клиента, где Meteor найдет это.

Изготовление шаблонов

В вашем каталоге layouts/ создайте новый каталог с именем application/ с файлом с именем ApplicationLayout.jsx внутри и вставьте в него следующий JSX:

import React, {Component} from 'react';
export default class ApplicationLayout extends Component {
    render() {
        return (
            <div className='app'>
                {this.props.page}
            </div>
        );
    }
}

Если вы писали React с ES6 до этого, это не должно выглядеть иначе, и это также не должно быть сумасшедшим скачком по сравнению с ES5.

На всякий случай вот что происходит: во-первых, мы импортируем React и React.Component из пакета NPM react, который мы добавили в ФСМ 2. Затем мы определяем новый экземпляр React.Component, который становится экспортомпо умолчаниюдля файла (чтобы мы могли импортировать его позже без фигурных скобок). Имя компонента включает тип компонента (в данном случаеlayout), чтобы при открытии файла в редакторе кода вы могли отличить его от других файлов с таким же именем. Единственная функция, которую имеет наш компонент, — это функция рендеринга, которая возвращает объект XML. Обратите внимание, что в JSX мы используем className вместо обычного атрибута класса HTML, потому что класс зарезервирован в JS. Внутри XML мы вставляем любой объект, который передается как реквизит с именем page в наш компонент.

Теперь давайте создадим новый каталог с именем welcome/ в каталоге pages/ с новым файлом WelcomePage.jsx и вставим в него следующий JSX:

import React, {Component} from 'react';
import Hello from '/imports/ui/components/hello/Hello.jsx';
import Info from '/imports/ui/components/info/Info.jsx';
export default class WelcomePage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0,
        };
        this.incrementCounter = this.incrementCounter.bind(this);
    }
    incrementCounter() {
        this.setState({
            counter: this.state.counter + 1,
        });
    }
    render() {
        return (
            <div>
                <h1>Welcome to Meteor!</h1>
                <Hello counter={this.state.counter}
                       incrementCounter={this.incrementCounter} />
                <Info />
            </div>
        );
    }
}

Не беспокойтесь о функциях вне render(), я перейду к ним через минуту. Во-первых, давайте создадим те два компонента, которые мы пытаемся импортировать, Hello и Info. В каталоге components/ создайте новый каталог для каждого и вставьте следующий JSX в components/hello/Hello.jsx:

import React, {Component} from 'react';
export default class Hello extends Component {
    render() {
        return (
            <div>
                <button onClick={this.props.incrementCounter}>Click Me</button>
                <p>You've pressed the button {this.props.counter} times.</p>
            </div>
        );
    }
}

и этот JSX в components/info/Info.jsx:

import React, {Component} from 'react';
export default class Info extends Component {
    render() {
        return (
            <div>
                <h2>Learn Meteor!</h2>
                <ul>
                    <li><a href="https://www.meteor.com/try" target="_blank">Do the Tutorial</a></li>
                    <li><a href="http://guide.meteor.com" target="_blank">Follow the Guide</a></li>
                    <li><a href="https://docs.meteor.com" target="_blank">Read the Docs</a></li>
                    <li><a href="https://forums.meteor.com" target="_blank">Discussions</a></li>
                </ul>
            </div>
        );
    }
}

Кроме того, форматирование имен: все файлы шаблонов JSX имеют имена в верхнем регистре, все остальные файлы JS(X) — в нижнем регистре. Все файлы (S)CSS и HTML имеют имена в нижнем регистре через дефис. Все каталоги названы строчными буквами с подчеркиванием. Это делает все легко анализируемым, действительным при упоминании в нашем коде и однозначно идентифицируемым.

Отслеживание логики

Потрясающий. Помните те функции, которые мы игнорировали ранее? Теперь вернемся к WelcomePage.jsx:

...
constructor(props) {
    super(props);
    this.state = {
        counter: 0,
    };
    this.incrementCounter = this.incrementCounter.bind(this);
}
incrementCounter() {
    this.setState({
        counter: this.state.counter + 1,
    });
}
...

Как и следовало ожидать, constructor() вызывается один раз при загрузке компонента страницы и никогда больше. В этом примере вы заметите, что мы используем состояние React вместо Redux (обещаю, мы до этого доберемся!)

Также стоит отметить, что для того, чтобы иметь возможность вызывать this.incrementCounter, мы должны явно связать функцию incrementCounter() с компонентом WelcomePage. В этом не было бы необходимости, если бы мы не надеялись использовать this где-либо в функции incrementCounter(), но, увы.

Чтобы быстро найти {this.props.X}, который вы заметили в Hello.jsx, взгляните на компонент Hello на странице приветствия:

<Hello counter={this.state.counter}
       incrementCounter={this.incrementCounter} />

Как видите, реквизиты передаются так же, как и обычные XML-атрибуты, и даже могут быть связанными функциями!

Убираться

Теперь вернемся к файлам client/, которые поставляются с нашей установкой Meteor. Мы поместили все в main.html в наш макет, страницу и компоненты, поэтому мы можем удалить весь его ‹body›. Тем не менее, мы оставим ‹head›, потому что именно здесь вы можете добавить все свои обычные метатеги, значки и тому подобное в будущем. Мы рассмотрим main.js в следующей статье!



Вы можете помочь сделать это руководство лучше, комментируя, когда что-то кажется неясным, бесполезным или просто неправильным. Пожалуйста, будьте ясны, подробны и сохраняйте позитивный настрой! Если у вас есть предложения для будущих статей или вопросы о руководстве в целом, пишите мне в Твиттере:@teaganatwaterБольшое спасибо!