Appearance
Meteor.js 3 + React
在本教程中,我们将使用 React 和 Meteor 3.0 创建一个简单的 To-Do 应用。Meteor 可以很好地与其他框架配合使用,例如 Blaze、Vue 3、Solid 和 Svelte。
¥In this tutorial, we will create a simple To-Do app using React and Meteor 3.0. Meteor works well with other frameworks like Blaze, Vue 3, Solid, and Svelte.
React 是一个用于构建用户界面的流行 JavaScript 库。它允许你通过组合 UI 组件来创建动态和交互式应用。React 使用声明式方法,你可以根据状态定义 UI 的外观,并在状态发生变化时有效地更新视图。使用 JSX(一种结合了 JavaScript 和 HTML 的语法扩展),React 可以轻松创建可重用的组件,这些组件可以管理自己的状态并在浏览器中无缝渲染。
¥React is a popular JavaScript library for building user interfaces. It allows you to create dynamic and interactive applications by composing UI components. React uses a declarative approach, where you define how the UI should look based on the state, and it efficiently updates the view when the state changes. With JSX, a syntax extension that combines JavaScript and HTML, React makes it easy to create reusable components that manage their own state and render seamlessly in the browser.
要开始构建你的 React 应用,你需要一个代码编辑器。如果你不确定选择哪一个,Visual Studio 代码 是一个不错的选择。安装后,你可以通过添加 Meteor 工具箱 等扩展来增强你的体验。
¥To start building your React app, you'll need a code editor. If you're unsure which one to choose, Visual Studio Code is a good option. After installing it, you can enhance your experience by adding extensions like Meteor Toolbox.
让我们开始构建你的应用!
¥Let’s begin building your app!
目录
¥Table of Contents
1:创建应用
¥1: Creating the app
1.1:安装 Meteor
¥1.1: Install Meteor
首先,我们需要安装 Meteor。
¥First, we need to install Meteor.
如果你尚未安装 Meteor,则可以通过运行以下命令进行安装:
¥If you don't have Meteor installed, you can install it by running:
shell
npx meteor
1.2:创建 Meteor 项目
¥1.2: Create Meteor Project
使用 React 设置 Meteor 的最简单方法是使用命令 meteor create
和选项 --react
以及你的项目名称(你也可以省略 --react
选项,因为它是默认选项):
¥The easiest way to setup Meteor with React is by using the command meteor create
with the option --react
and your project name (you can also omit the --react
option since it is the default):
shell
meteor create simple-todos-react
Meteor 将为你创建所有必要的文件。
¥Meteor will create all the necessary files for you.
位于 client
目录中的文件正在设置你的客户端(Web),例如,你可以看到 client/main.jsx
,其中 Meteor 正在将你的 App 主要组件渲染到 HTML 中。
¥The files located in the client
directory are setting up your client side (web), you can see for example client/main.jsx
where Meteor is rendering your App main component into the HTML.
另外,检查 Meteor 设置服务器端 (Node.js) 的 server
目录,你可以看到 server/main.js
正在用一些数据初始化你的 MongoDB 数据库。你不需要安装 MongoDB,因为 Meteor 提供了一个可供你使用的嵌入式版本。
¥Also, check the server
directory where Meteor is setting up the server side (Node.js), you can see the server/main.js
is initializing your MongoDB database with some data. You don't need to install MongoDB as Meteor provides an embedded version of it ready for you to use.
你现在可以使用以下命令运行 Meteor 应用:
¥You can now run your Meteor app using:
shell
meteor run
不用担心,从现在开始,Meteor 将使你的应用与所有更改保持同步。
¥Don't worry, Meteor will keep your app in sync with all your changes from now on.
你的 React 代码将位于 imports/ui
目录中,App.jsx
文件是你的 React To-do 应用的根组件。
¥Your React code will be located inside the imports/ui
directory, and App.jsx
file is the root component of your React To-do app.
快速浏览 Meteor 创建的所有文件,你现在不需要了解它们,但知道它们在哪里会很好。
¥Take a quick look at all the files created by Meteor, you don't need to understand them now but it's good to know where they are.
1.3:创建任务组件
¥1.3: Create Task Component
你现在将进行第一次更改。在 ui
文件夹中创建一个名为 Task.jsx
的新文件。
¥You will make your first change now. Create a new file called Task.jsx
in your ui
folder.
此文件将导出一个名为 Task
的 React 组件,该组件将代表你的待办事项列表中的一项任务。
¥This file will export a React component called Task
that will represent one task in your To-Do list.
js
import React from "react";
export const Task = ({ task }) => {
return <li>{task.text}</li>;
};
由于此组件将位于列表中,因此你将返回 li
元素。
¥As this component will be inside a list you are returning a li
element.
1.4:创建示例任务
¥1.4: Create Sample Tasks
由于你尚未连接到服务器和数据库,让我们定义一些示例数据,这些数据将很快用于渲染任务列表。它将是一个数组,你可以将它称为 tasks
。
¥As you are not connecting to your server and your database yet let's define some sample data which will be used shortly to render a list of tasks. It will be an array, and you can call it tasks
.
js
import React from 'react';
const tasks = [
{_id: 1, text: 'First Task'},
{_id: 2, text: 'Second Task'},
{_id: 3, text: 'Third Task'},
];
export const App = () => ...
你可以将任何内容作为每个任务的 text
属性。发挥创意!
¥You can put anything as your text
property on each task. Be creative!
1.5:渲染示例任务
¥1.5: Render Sample Tasks
现在我们可以使用 React 实现一些简单的渲染逻辑。我们现在可以使用我们之前的 Task
组件来渲染我们的列表项。
¥Now we can implement some simple rendering logic with React. We can now use our previous Task
component to render our list items.
在 React 中,你可以使用 {
}
在它们之间编写 Javascript 代码。
¥In React you can use {
}
to write Javascript code between them.
请参见下文,你将使用 Array
对象中的 .map
函数来迭代示例任务。
¥See below that you will use a .map
function from the Array
object to iterate over your sample tasks.
js
import React from 'react';
import { Task } from './Task';
const tasks = ..;
export const App = () => (
<div>
<h1>Welcome to Meteor!</h1>
<ul>
{ tasks.map(task => <Task key={ task._id } task={ task }/>) }
</ul>
</div>
);
请记住将 key
属性添加到你的任务中,否则 React 会发出警告,因为它会看到许多相同类型的组件作为同级组件。如果没有密钥,React 将很难在必要时重新渲染其中一个。
¥Remember to add the key
property to your task, otherwise React will emit a warning because it will see many components of the same type as siblings. Without a key, it will be hard for React to re-render one of them if necessary.
你可以阅读有关 React 和 Keys 此处 的更多信息。
¥You can read more about React and Keys here.
从 App
组件中删除 Hello
和 Info
,记得还要删除文件顶部的导入。同时删除 Hello.jsx
和 Info.jsx
文件。
¥Remove the Hello
and Info
from your App
component, remember to also remove the imports for them at the top of the file. Remove the Hello.jsx
and Info.jsx
files as well.
1.6:热模块替换
¥1.6: Hot Module Replacement
默认情况下,使用 React 时,Meteor 已经为你添加了一个名为 hot-module-replacement
的包。此软件包更新正在运行的应用中在重建期间修改的 javascript 模块。减少开发过程中的反馈周期,以便你可以更快地查看和测试更改(它甚至在构建完成之前更新应用)。你也不会丢失状态,你的应用代码将被更新,你的状态将保持不变。
¥Meteor by default when using React is already adding for you a package called hot-module-replacement
. This package updates the javascript modules in a running app that were modified during a rebuild. Reduces the feedback cycle while developing so you can view and test changes quicker (it even updates the app before the build has finished). You are also not going to lose the state, your app code will be updated and your state will be the same.
在下一步中,我们将使用我们的 MongoDB 数据库来存储我们的任务。
¥In the next step, we are going to work with our MongoDB database to store our tasks.
2:集合
¥2: Collections
Meteor 已经为你设置了 MongoDB。为了使用我们的数据库,我们需要创建一个集合,我们将在其中存储我们的文档,在我们的例子中是 tasks
。
¥Meteor already sets up MongoDB for you. In order to use our database, we need to create a collection, which is where we will store our documents, in our case our tasks
.
你可以阅读有关集合 此处 的更多信息。
¥You can read more about collections here.
在此步骤中,我们将实现所有必要的代码,以便使用 React hooks 为我们的任务建立一个基本集合。
¥In this step, we will implement all the necessary code to have a basic collection for our tasks up and running using React hooks.
2.1:创建任务集合
¥2.1: Create Tasks Collection
我们可以通过在 imports/api/TasksCollection.js
处创建一个新文件来创建一个新集合来存储我们的任务,该文件实例化一个新的 Mongo 集合并将其导出。
¥We can create a new collection to store our tasks by creating a new file at imports/api/TasksCollection.js
which instantiates a new Mongo collection and exports it.
js
import { Mongo } from "meteor/mongo";
export const TasksCollection = new Mongo.Collection("tasks");
请注意,我们将文件存储在 imports/api
目录中,该目录是存储 API 相关代码(如发布物和方法)的地方。你可以根据需要命名此文件夹,这只是一个选择。
¥Notice that we stored the file in the imports/api
directory, which is a place to store API-related code, like publications and methods. You can name this folder as you want, this is just a choice.
你可以删除此文件夹中的 links.js
文件,因为我们不会使用此集合。
¥You can delete the links.js
file in this folder as we are not going to use this collection.
你可以阅读有关应用结构和导入/导出 此处 的更多信息。
¥You can read more about app structure and imports/exports here.
2.2:初始化任务集合
¥2.2: Initialize Tasks Collection
为了使我们的集合正常工作,你需要将其导入服务器,以便设置一些管道。
¥For our collection to work, you need to import it in the server so it sets some plumbing up.
如果你要在同一个文件上使用,你可以使用 import "/imports/api/TasksCollection"
或 import { TasksCollection } from "/imports/api/TasksCollection"
,但请确保它已导入。
¥You can either use import "/imports/api/TasksCollection"
or import { TasksCollection } from "/imports/api/TasksCollection"
if you are going to use on the same file, but make sure it is imported.
现在很容易检查我们的集合中是否有数据,否则,我们也可以轻松插入一些示例数据。
¥Now it is easy to check if there is data or not in our collection, otherwise, we can insert some sample data easily as well.
你不需要保留 server/main.js
的旧内容。
¥You don't need to keep the old content of server/main.js
.
js
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "/imports/api/TasksCollection";
const insertTask = (taskText) =>
TasksCollection.insertAsync({ text: taskText });
Meteor.startup(async () => {
if ((await TasksCollection.find().countAsync()) === 0) {
[
"First Task",
"Second Task",
"Third Task",
"Fourth Task",
"Fifth Task",
"Sixth Task",
"Seventh Task",
].forEach(insertTask);
}
});
因此,你正在导入 TasksCollection
并向其添加一些任务,遍历字符串数组,并为每个字符串调用一个函数将此字符串作为我们的 text
字段插入到我们的 task
文档中。
¥So you are importing the TasksCollection
and adding a few tasks to it iterating over an array of strings and for each string calling a function to insert this string as our text
field in our task
document.
2.3:渲染任务集合
¥2.3: Render Tasks Collection
现在到了有趣的部分,你将使用 React 函数组件和来自 react-meteor-data 包的名为 useTracker
的 Hook 来渲染任务。
¥Now comes the fun part, you will render the tasks using a React Function Component and a Hook called useTracker
from a package called react-meteor-data.
Meteor 与 Meteor 包和 NPM 包一起工作,通常,Meteor 包使用 Meteor 内部或其他 Meteor 包。
¥Meteor works with Meteor packages and NPM packages, usually, Meteor packages are using Meteor internals or other Meteor packages.
此包已包含在 React 骨架(meteor create yourproject
)中,因此你不需要添加它,但你始终可以添加运行 meteor add package-name
的 Meteor 包:
¥This package is already included in the React skeleton (meteor create yourproject
) so you don't need to add it but you can always add Meteor packages running meteor add package-name
:
shell
meteor add react-meteor-data
现在你已准备好从此包导入代码,当从 Meteor 包导入代码时,与 NPM 模块的唯一区别是你需要在导入的 from 部分中添加 meteor/
。
¥Now you are ready to import code from this package, when importing code from a Meteor package the only difference from NPM modules is that you need to prepend meteor/
in the from part of your import.
react-meteor-data
导出的 useTracker
函数是一个 React Hook,可让你在 React 组件中具有反应性。每次数据通过反应性发生变化时,你的组件都会重新渲染。很酷吧?
¥The useTracker
function exported by react-meteor-data
is a React Hook that allows you to have reactivity in your React components. Every time the data changes through reactivity your component will re-render. Cool, right?
有关 React Hooks 的更多信息,请阅读 此处。
¥For more information about React Hooks read here.
javascript
import React from "react";
import { useTracker } from "meteor/react-meteor-data";
import { TasksCollection } from "/imports/api/TasksCollection";
import { Task } from "./Task";
export const App = () => {
const tasks = useTracker(() => TasksCollection.find({}).fetch());
return (
<div>
<h1>Welcome to Meteor!</h1>
<ul>
{tasks.map((task) => (
<Task key={task._id} task={task} />
))}
</ul>
</div>
);
};
但请稍等!缺少某些内容。如果你现在运行你的应用,你将看到你没有渲染任何任务。
¥But wait! Something is missing. If you run your app now, you'll see that you don't render any tasks.
这是因为我们需要将数据发布到客户端。
¥That's because we need to publish our data to the client.
首先,为我们的任务创建一个发布:
¥Fist, create a publication for our tasks:
imports/api/TasksPublications.js
javascript
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "./TasksCollection";
Meteor.publish("tasks", () => {
return TasksCollection.find();
});
现在,我们需要将此文件导入我们的服务器:
¥Now, we need to import this file in our server:
js
...
import { TasksCollection } from '/imports/api/TasksCollection';
import "../imports/api/TasksPublications";
const insertTask = taskText => TasksCollection.insertAsync({ text: taskText });
...
剩下唯一要做的事情就是订阅此发布:
¥The only thing left is subscribe to this publication:
imports/ui/App.jsx
javascript
import React from 'react';
import { useTracker, useSubscribe } from 'meteor/react-meteor-data';
import { TasksCollection } from '/imports/api/TasksCollection';
import { Task } from './Task';
export const App = () => {
const isLoading = useSubscribe("tasks");
const tasks = useTracker(() => TasksCollection.find({}).fetch());
if (isLoading()) {
return <div>Loading...</div>;
}
...
}
如你所见,当使用 useSubscribe
订阅发布物时,你将获得一个 isLoading
函数,你可以使用它在数据准备好之前渲染一些加载组件。
¥As you can see, when subscribing to a publication using useSubscribe
you'll get a isLoading
function, that you can use to render some loading component before the data is ready.
有关发布物/订阅的更多信息,请查看我们的 docs。
¥For more information on Publications/Subscriptions, please check our docs.
查看你的应用现在应该是什么样子:
¥See how your app should look like now:
你可以在服务器上更改 MongoDB 上的数据,你的应用将做出反应并为你重新渲染。
¥You can change your data on MongoDB in the server and your app will react and re-render for you.
你可以从应用文件夹或使用 Mongo UI 客户端(如 NoSQLBooster)连接到在终端中运行 meteor mongo
的 MongoDB。你的嵌入式 MongoDB 正在端口 3001
中运行。
¥You can connect to your MongoDB running meteor mongo
in the terminal from your app folder or using a Mongo UI client, like NoSQLBooster. Your embedded MongoDB is running in port 3001
.
查看如何连接:
¥See how to connect:
查看你的数据库:
¥See your database:
你可以双击你的集合以查看存储在其中的文档:
¥You can double-click your collection to see the documents stored on it:
在下一步中,我们将使用表单创建任务。
¥In the next step, we are going to create tasks using a form.
3:表单和事件
¥3: Forms and Events
所有应用都需要允许用户与存储的数据进行某种交互。在我们的例子中,第一种交互类型是插入新任务。没有它,我们的 To-Do 应用就不会很有用。
¥All apps need to allow the user to perform some sort of interaction with the data that is stored. In our case, the first type of interaction is to insert new tasks. Without it, our To-Do app wouldn't be very helpful.
用户在网站上插入或编辑数据的主要方式之一是通过表单。在大多数情况下,使用 <form>
标签是个好主意,因为它赋予其中的元素语义。
¥One of the main ways in which a user can insert or edit data on a website is through forms. In most cases, it is a good idea to use the <form>
tag since it gives semantic meaning to the elements inside it.
3.1:创建任务表单
¥3.1: Create Task Form
首先,我们需要创建一个简单的表单组件来封装我们的逻辑。如你所见,我们设置了 useState
React Hook。
¥First, we need to create a simple form component to encapsulate our logic. As you can see we set up the useState
React Hook.
请注意数组解构 [text, setText]
,其中 text
是我们要使用的存储值,在本例中将是一个字符串;setText
是用于更新该值的函数。
¥Please note the array destructuring [text, setText]
, where text
is the stored value which we want to use, which in this case will be a string; and setText
is a function used to update that value.
在 ui
文件夹中创建一个新文件 TaskForm.jsx
。
¥Create a new file TaskForm.jsx
in your ui
folder.
js
import React, { useState } from "react";
export const TaskForm = () => {
const [text, setText] = useState("");
return (
<form className="task-form">
<input type="text" placeholder="Type to add new tasks" />
<button type="submit">Add Task</button>
</form>
);
};
3.2:更新应用组件
¥3.2: Update the App component
然后我们可以简单地将其添加到任务列表上方的 App
组件中:
¥Then we can simply add this to our App
component above your list of tasks:
js
import React from "react";
import { useTracker, useSubscribe } from "meteor/react-meteor-data";
import { TasksCollection } from "/imports/api/TasksCollection";
import { Task } from "./Task";
import { TaskForm } from "./TaskForm";
export const App = () => {
const isLoading = useSubscribe("tasks");
const tasks = useTracker(() => TasksCollection.find({}).fetch());
if (isLoading()) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Welcome to Meteor!</h1>
<TaskForm />
<ul>
{tasks.map((task) => (
<Task key={task._id} task={task} />
))}
</ul>
</div>
);
};
3.3:更新样式表
¥3.3: Update the Stylesheet
你还可以根据需要设置其样式。目前,我们只需要顶部的一些边距,这样表单就不会显得偏离目标。添加 CSS 类 .task-form
,这需要与表单组件中 className
属性中的名称相同。
¥You also can style it as you wish. For now, we only need some margin at the top so the form doesn't seem off the mark. Add the CSS class .task-form
, this needs to be the same name in your className
attribute in the form component.
css
.task-form {
margin-top: 1rem;
}
3.4:添加提交处理程序
¥3.4: Add Submit Handler
现在让我们创建一个函数来处理表单提交并将新任务插入数据库。为此,我们需要实现一个 Meteor 方法。
¥Now let's create a function to handle the form submit and insert a new task into the database. To do it, we will need to implement a Meteor Method.
方法本质上是对服务器的 RPC 调用,可让你安全地在服务器端执行操作。你可以阅读有关 Meteor 方法 此处 的更多信息。
¥Methods are essentially RPC calls to the server that let you perform operations on the server side securely. You can read more about Meteor Methods here.
要创建方法,你可以创建一个名为 tasksMethods.js
的文件。
¥To create your methods, you can create a file called tasksMethods.js
.
javascript
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "./TasksCollection";
Meteor.methods({
"tasks.insert"(doc) {
return TasksCollection.insertAsync(doc);
},
});
请记住在 main.js
服务器文件和 main.jsx
客户端文件上导入你的方法。
¥Remember to import your method on the main.js
server file and the main.jsx
client file.
javascript
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "../imports/api/tasksCollection";
import "../imports/api/TasksPublications";
import "../imports/api/tasksMethods";
javascript
import React from "react";
import { createRoot } from "react-dom/client";
import { Meteor } from "meteor/meteor";
import { App } from "/imports/ui/App";
import "../imports/api/tasksMethods";
现在你可以使用 onSubmit
事件将提交处理程序附加到表单,并将你的 React Hook 插入输入元素中的 onChange
事件。
¥Now you can attach a submit handler to your form using the onSubmit
event, and also plug your React Hook into the onChange
event present in the input element.
如你所见,你正在使用 useState
React Hook 来存储 <input>
元素的 value
。请注意,你还需要将 value
属性设置为 text
常量,这将允许 input
元素与我们的钩子保持同步。
¥As you can see you are using the useState
React Hook to store the value
of your <input>
element. Note that you also need to set your value
attribute to the text
constant as well, this will allow the input
element to stay in sync with our hook.
在更复杂的应用中,如果在可能频繁发生的事件(如
onChange
)之间发生许多计算,你可能需要实现一些debounce
或throttle
逻辑。有些库可以帮助你实现这一点,例如 Lodash。¥In more complex applications you might want to implement some
debounce
orthrottle
logic if there are many calculations happening between potentially frequent events likeonChange
. There are libraries which will help you with this, like Lodash, for instance.
js
import React, { useState } from "react";
import { TasksCollection } from "/imports/api/TasksCollection";
export const TaskForm = () => {
const [text, setText] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
if (!text) return;
await Meteor.callAsync("tasks.insert", {
text: text.trim(),
createdAt: new Date(),
});
setText("");
};
return (
<form className="task-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Type to add new tasks"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button type="submit">Add Task</button>
</form>
);
};
在函数内部,我们通过调用 Meteor.callAsync()
将任务添加到 tasks
集合。第一个参数是我们要调用的方法的名称,第二个参数是任务的文本。我们还在修剪文本以删除任何多余的空格。
¥Inside the function, we are adding a task to the tasks
collection by calling Meteor.callAsync()
. The first argument is the name of the method we want to call, and the second argument is the text of the task. We are also trimming the text to remove any extra spaces.
另外,在你的 task
文档中插入一个日期 createdAt
,这样你就知道每个任务的创建时间。
¥Also, insert a date createdAt
in your task
document so you know when each task was created.
3.5:首先显示最新任务
¥3.5: Show Newest Tasks First
现在,你只需要进行一些让用户满意的更改:我们需要先显示最新任务。我们可以通过对 Mongo 查询进行排序来快速完成此操作。
¥Now you just need to make a change that will make users happy: we need to show the newest tasks first. We can accomplish this quite quickly by sorting our Mongo query.
js
..
export const App = () => {
const tasks = useTracker(() => TasksCollection.find({}, { sort: { createdAt: -1 } }).fetch());
..
你的应用应如下所示:
¥Your app should look like this:
在下一步中,我们将更新你的任务状态并为用户提供一种删除任务的方法。
¥In the next step, we are going to update your tasks state and provide a way for users to remove tasks.
4:更新和删除
¥4: Update and Remove
到目前为止,你只将文档插入到我们的集合中。让我们看看如何通过与用户界面交互来更新和删除它们。
¥Up until now, you have only inserted documents into our collection. Let's take a look at how you can update and remove them by interacting with the user interface.
4.1:添加复选框
¥4.1: Add Checkbox
首先,你需要向你的 Task
组件添加一个 checkbox
元素。
¥First, you need to add a checkbox
element to your Task
component.
请务必添加
readOnly
属性,因为我们不使用onChange
来更新状态。¥Be sure to add the
readOnly
attribute since we are not usingonChange
to update the state.我们还必须将我们的
checked
prop 强制为boolean
,因为 React 知道undefined
值不存在,因此导致组件从不受控切换到受控。¥We also have to force our
checked
prop to aboolean
since React understands that anundefined
value as inexistent, therefore causing the component to switch from uncontrolled to a controlled one.我们还邀请你进行实验,并查看应用如何表现以供学习。
¥You are also invited to experiment and see how the app behaves for learning purposes.
你还想接收一个回调,即单击复选框时将调用的函数。
¥You also want to receive a callback, a function that will be called when the checkbox is clicked.
js
import React from "react";
export const Task = ({ task, onCheckboxClick }) => {
return (
<li>
<input
type="checkbox"
checked={!!task.isChecked}
onClick={() => onCheckboxClick(task)}
readOnly
/>
<span>{task.text}</span>
</li>
);
};
4.2:切换复选框
¥4.2: Toggle Checkbox
现在,你可以通过切换其 isChecked
字段来更新你的任务文档。
¥Now you can update your task document by toggling its isChecked
field.
首先,创建一个名为 tasks.toggleChecked
的新方法来更新 isChecked
属性。
¥First, create a new method called tasks.toggleChecked
to update the isChecked
property.
javascript
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "./TasksCollection";
Meteor.methods({
..
"tasks.toggleChecked"({ _id, isChecked }) {
return TasksCollection.updateAsync(_id, {
$set: { isChecked: !isChecked },
});
},
});
现在,创建一个函数来更改你的文档并将其传递给你的 Task
组件。
¥Now, create a function to change your document and pass it along to your Task
component.
js
..
export const App = () => {
const handleToggleChecked = ({ _id, isChecked }) =>
Meteor.callAsync("tasks.toggleChecked", { _id, isChecked });
..
<ul>
{ tasks.map(task => <Task key={ task._id } task={ task } onCheckboxClick={handleToggleChecked} />) }
</ul>
..
你的应用应如下所示:
¥Your app should look like this:
4.3:删除任务
¥4.3: Remove tasks
你只需几行代码即可删除任务。
¥You can remove tasks with just a few lines of code.
首先,在 Task
组件中的文本后添加一个按钮并接收回调函数。
¥First, add a button after text in your Task
component and receive a callback function.
js
import React from 'react';
export const Task = ({ task, onCheckboxClick, onDeleteClick }) => {
return (
..
<span>{task.text}</span>
<button onClick={ () => onDeleteClick(task) }>×</button>
..
现在在 App
中添加删除逻辑,你需要有一个函数来删除任务,并在 Task
组件中的回调属性中提供此函数。
¥Now add the removal logic in the App
, you need to have a function to delete the task and provide this function in your callback property in the Task
component.
为此,让我们创建一个名为 tasks.delete
的新方法:
¥For that, let's create a new method called tasks.delete
:
javascript
import { Meteor } from "meteor/meteor";
import { TasksCollection } from "./TasksCollection";
Meteor.methods({
..
"tasks.delete"({ _id }) {
return TasksCollection.removeAsync(_id);
},
});
然后,让我们在 handleDelete
函数内调用此方法:
¥Then, let's call this method inside a handleDelete
function:
js
export const App = () => {
..
const handleDelete = ({ _id }) =>
Meteor.callAsync("tasks.delete", { _id });
..
<ul>
{ tasks.map(task => <Task
key={ task._id }
task={ task }
onCheckboxClick={handleToggleChecked}
onDeleteClick={handleDelete}
/>) }
</ul>
..
}
你的应用应如下所示:
¥Your app should look like this:
在下一步中,我们将使用带有 Flexbox 的 CSS 改善应用的外观。
¥In the next step, we are going to improve the look of your app using CSS with Flexbox.
5:样式
¥5: Styles
5.1:CSS
到目前为止,我们的用户界面看起来相当丑陋。让我们添加一些基本样式,作为更专业的应用的基础。
¥Our user interface up until this point has looked quite ugly. Let's add some basic styling which will serve as the foundation for a more professional looking app.
用下面的内容替换我们的 client/main.css
文件的内容,想法是在顶部有一个应用栏,以及一个可滚动的内容,包括:
¥Replace the content of our client/main.css
file with the one below, the idea is to have an app bar at the top, and a scrollable content including:
添加新任务的表单;
¥form to add new tasks;
任务列表。
¥list of tasks.
css
body {
font-family: sans-serif;
background-color: #315481;
background-image: linear-gradient(to bottom, #315481, #918e82 100%);
background-attachment: fixed;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 0;
margin: 0;
font-size: 14px;
}
button {
font-weight: bold;
font-size: 1em;
border: none;
color: white;
box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4);
padding: 5px;
cursor: pointer;
}
button:focus {
outline: 0;
}
.app {
display: flex;
flex-direction: column;
height: 100vh;
}
.app-header {
flex-grow: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.main {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: auto;
background: white;
}
.main::-webkit-scrollbar {
width: 0;
height: 0;
background: inherit;
}
header {
background: #d2edf4;
background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
padding: 20px 15px 15px 15px;
position: relative;
box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4);
}
.app-bar {
display: flex;
justify-content: space-between;
}
.app-bar h1 {
font-size: 1.5em;
margin: 0;
display: inline-block;
margin-right: 1em;
}
.task-form {
display: flex;
margin: 16px;
}
.task-form > input {
flex-grow: 1;
box-sizing: border-box;
padding: 10px 6px;
background: transparent;
border: 1px solid #aaa;
width: 100%;
font-size: 1em;
margin-right: 16px;
}
.task-form > input:focus {
outline: 0;
}
.task-form > button {
min-width: 100px;
height: 95%;
background-color: #315481;
}
.tasks {
list-style-type: none;
padding-inline-start: 0;
padding-left: 16px;
padding-right: 16px;
margin-block-start: 0;
margin-block-end: 0;
}
.tasks > li {
display: flex;
padding: 16px;
border-bottom: #eee solid 1px;
align-items: center;
}
.tasks > li > span {
flex-grow: 1;
}
.tasks > li > button {
justify-self: flex-end;
background-color: #ff3046;
}
如果你想了解有关此样式表的更多信息,请查看有关 Flexbox 的这篇文章,以及来自 Wes Bos 的关于它的免费 视频教程。
¥If you want to learn more about this stylesheet check this article about Flexbox, and also this free video tutorial about it from Wes Bos.
Flexbox 是分发和对齐 UI 中元素的绝佳工具。
¥Flexbox is an excellent tool to distribute and align elements in your UI.
5.2:应用样式
¥5.2: Applying styles
现在,你需要在组件周围添加一些元素。你将在 App
中的主 div 中添加一个 className
,在 h1
周围添加一个 header
元素和一些 divs
,在表单和列表周围添加一个主 div
。检查下面应该如何,注意类的名称,它们需要与 CSS 文件中的名称相同:
¥Now you need to add some elements around your components. You are going to add a className
to your main div in the App
, also a header
element with a few divs
around your h1
, and a main div
around your form and list. Check below how it should be, pay attention to the name of the classes, they need to be the same as in the CSS file:
js
..
return (
<div className="app">
<header>
<div className="app-bar">
<div className="app-header">
<h1>Welcome to Meteor!</h1>
</div>
</div>
</header>
<div className="main">
<TaskForm />
<ul className="tasks">
{tasks.map((task) => (
<Task
key={task._id}
task={task}
onCheckboxClick={handleToggleChecked}
onDeleteClick={handleDelete}
/>
))}
</ul>
</div>
</div>
);
在 React 中,我们使用
className
而不是class
,因为 React 使用 Javascript 来定义 UI,而class
是 Javascript 中的保留字。¥In React we use
className
instead ofclass
as React uses Javascript to define the UI andclass
is a reserved word in Javascript.
另外,为你的应用选择一个更好的标题,Meteor 很棒,但你不想一直在应用顶部栏看到 Welcome to Meteor!
。
¥Also, choose a better title for your app, Meteor is amazing but you don't want to see Welcome to Meteor!
in your app top bar all the time.
你可以选择类似以下内容:
¥You could choose something like:
js
..
<h1>📝️ To Do List</h1>
..
你的应用应如下所示:
¥Your app should look like this:
在下一步中,我们将使此任务列表更具交互性,例如,提供一种过滤任务的方法。
¥In the next step, we are going to make this task list more interactive, for example, providing a way to filter tasks.
6:过滤任务
¥6: Filter tasks
在此步骤中,你将按状态过滤任务并显示待处理任务的数量。
¥In this step, you will filter your tasks by status and show the number of pending tasks.
6.1:useState
首先,你要添加一个按钮来显示或隐藏列表中已完成的任务。
¥First, you are going to add a button to show or hide the completed tasks from the list.
React 中的 useState
函数是保持此按钮状态的最佳方式。它返回一个包含两个项目的数组,其中第一个元素是状态的值,第二个元素是设置函数,这是你将如何更新状态。你可以使用数组解构来取回这两个并为它们声明一个变量。
¥The useState
function from React is the best way to keep the state of this button. It returns an array with two items, where the first element is the value of the state, and the second is a setter function that is how you are going to update your state. You can use array destructuring to get these two back and already declare a variable for them.
请记住,用于常量的名称不属于 React API,你可以随意命名它们。
¥Bear in mind that the names used for the constants do not belong to the React API, you can name them whatever you like.
此外,在任务表单下方添加一个 button
,它将根据当前状态显示不同的文本。
¥Also, add a button
below the task form that will display a different text based on the current state.
js
import React, { useState } from 'react';
..
export const App = () => {
const [hideCompleted, setHideCompleted] = useState(false);
..
<div className="main">
<TaskForm />
<div className="filter">
<button onClick={() => setHideCompleted(!hideCompleted)}>
{hideCompleted ? 'Show All' : 'Hide Completed'}
</button>
</div>
..
你可以阅读有关 useState
钩子 此处 的更多信息。
¥You can read more about the useState
hook here.
我们建议你始终在组件的顶部添加钩子,这样可以更容易地避免一些问题,例如始终以相同的顺序运行它们。
¥We recommend that you add your hooks always in the top of your components, so it will be easier to avoid some problems, like always running them in the same order.
6.2:按钮样式
¥6.2: Button style
你应该为按钮添加一些样式,使其看起来不会是灰色的并且没有很好的对比度。你可以使用以下样式作为参考:
¥You should add some style to your button so it does not look gray and without a good contrast. You can use the styles below as a reference:
css
.filter {
display: flex;
justify-content: center;
}
.filter > button {
background-color: #62807e;
}
6.3:过滤任务
¥6.3: Filter Tasks
现在,如果用户只想查看待处理的任务,你可以在 Mini Mongo 查询中向选择器添加一个过滤器,你希望获取所有不为 isChecked
的任务。
¥Now, if the user wants to see only pending tasks you can add a filter to your selector in the Mini Mongo query, you want to get all the tasks that are not isChecked
true.
js
..
const hideCompletedFilter = { isChecked: { $ne: true } };
const tasks = useTracker(() =>
TasksCollection.find(hideCompleted ? hideCompletedFilter : {}, {
sort: { createdAt: -1 },
}).fetch()
);
..
6.4:Meteor Dev Tools 扩展
¥6.4: Meteor Dev Tools Extension
你可以安装扩展程序以可视化 Mini Mongo 中的数据。
¥You can install an extension to visualize the data in your Mini Mongo.
Meteor DevTools Evolved 将帮助你调试应用,因为你可以看到 Mini Mongo 上有什么数据。
¥Meteor DevTools Evolved will help you to debug your app as you can see what data is on Mini Mongo.
你还可以查看 Meteor 从服务器发送和接收的所有消息,这对你了解 Meteor 的工作原理很有用。
¥You can also see all the messages that Meteor is sending and receiving from the server, this is useful for you to learn more about how Meteor works.
使用此 link 将其安装在你的 Google Chrome 浏览器中。
¥Install it in your Google Chrome browser using this link.
6.5:待处理任务
¥6.5: Pending tasks
更新 App 组件以显示应用栏中待处理任务的数量。
¥Update the App component in order to show the number of pending tasks in the app bar.
当没有待处理任务时,你应该避免在应用栏中添加零。
¥You should avoid adding zero to your app bar when there are no pending tasks.
js
..
const pendingTasksCount = useTracker(() =>
TasksCollection.find(hideCompletedFilter).count()
);
const pendingTasksTitle = `${
pendingTasksCount ? ` (${pendingTasksCount})` : ''
}`;
..
<h1>
📝️ To Do List
{pendingTasksTitle}
</h1>
..
你可以在同一个 useTracker
中执行两次查找,然后返回具有两个属性的对象,但为了让代码更容易理解,我们在这里创建了两个不同的跟踪器。
¥You could do both finds in the same useTracker
and then return an object with both properties but to have a code that is easier to understand, we created two different trackers here.
你的应用应如下所示:
¥Your app should look like this:
下一步,我们将在你的应用中包含用户访问权限。
¥In the next step we are going to include user access in your app.
7:添加用户账户
¥7: Adding User Accounts
7.1:密码身份验证
¥7.1: Password Authentication
Meteor 已经自带了一个基本的开箱即用的身份验证和账户管理系统,因此你只需添加 accounts-password
即可启用用户名和密码身份验证:
¥Meteor already comes with a basic authentication and account management system out of the box, so you only need to add the accounts-password
to enable username and password authentication:
shell
meteor add accounts-password
支持更多身份验证方法。你可以阅读有关账户系统 此处 的更多信息。
¥There are many more authentication methods supported. You can read more about the accounts system here.
我们还建议你安装 bcrypt
Node 模块,否则你将看到一条警告,提示你正在使用它的纯 Javascript 实现。
¥We also recommend you to install bcrypt
node module, otherwise, you are going to see a warning saying that you are using a pure-Javascript implementation of it.
shell
meteor npm install --save bcrypt
你应该始终使用
meteor npm
而不是仅使用npm
,因此你始终使用 Meteor 固定的npm
版本,这有助于你避免由于不同版本的 npm 安装不同的模块而导致的问题。¥You should always use
meteor npm
instead of onlynpm
so you always use thenpm
version pinned by Meteor, this helps you to avoid problems due to different versions of npm installing different modules.
7.2:创建用户账户
¥7.2: Create User Account
现在你可以为我们的应用创建一个默认用户,我们将使用 meteorite
作为用户名,如果我们在数据库中找不到它,我们只需在服务器启动时创建一个新用户。
¥Now you can create a default user for our app, we are going to use meteorite
as username, we just create a new user on server startup if we didn't find it in the database.
js
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { TasksCollection } from '/imports/api/TasksCollection';
..
const SEED_USERNAME = 'meteorite';
const SEED_PASSWORD = 'password';
Meteor.startup(async () => {
if (!(await Accounts.findUserByUsername(SEED_USERNAME))) {
await Accounts.createUser({
username: SEED_USERNAME,
password: SEED_PASSWORD,
});
}
..
});
你目前不应该在应用 UI 中看到任何不同。
¥You should not see anything different in your app UI yet.
7.3:登录表单
¥7.3: Login Form
你需要为用户提供一种输入凭据和身份验证的方式,为此我们需要一个表单。
¥You need to provide a way for the users to input the credentials and authenticate, for that we need a form.
我们可以使用 useState
钩子来实现它。创建一个名为 LoginForm.jsx
的新文件并向其中添加表单。你应该使用 Meteor.loginWithPassword(username, password);
通过提供的输入对你的用户进行身份验证。
¥We can implement it using useState
hook. Create a new file called LoginForm.jsx
and add a form to it. You should use Meteor.loginWithPassword(username, password);
to authenticate your user with the provided inputs.
js
import { Meteor } from "meteor/meteor";
import React, { useState } from "react";
export const LoginForm = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const submit = (e) => {
e.preventDefault();
Meteor.loginWithPassword(username, password);
};
return (
<form onSubmit={submit} className="login-form">
<label htmlFor="username">Username</label>
<input
type="text"
placeholder="Username"
name="username"
required
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor="password">Password</label>
<input
type="password"
placeholder="Password"
name="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Log In</button>
</form>
);
};
好的,现在你有了一个表单,让我们使用它。
¥Ok, now you have a form, let's use it.
7.4:需要身份验证
¥7.4: Require Authentication
我们的应用应该只允许经过身份验证的用户访问其任务管理功能。
¥Our app should only allow an authenticated user to access its task management features.
我们可以通过在没有经过身份验证的用户时返回 LoginForm
组件来实现这一点,否则我们将返回表单、过滤器和列表组件。
¥We can accomplish that by returning the LoginForm
component when we don't have an authenticated user, otherwise we return the form, filter, and list component.
你应该首先将 3 个组件(表单、过滤器和列表)封装在 <Fragment>
中,Fragment 是 React 中的特殊组件,你可以使用它将组件组合在一起而不会影响最终的 DOM,这意味着不会影响你的 UI,因为它不会在 HTML 中引入其他元素。
¥You should first wrap the 3 components (form, filter, and list) in a <Fragment>
, Fragment is a special component in React that you can use to group components together without affecting your final DOM, it means without affecting your UI as it is not going to introduce other elements in the HTML.
阅读有关 Fragments 此处 的更多信息
¥Read more about Fragments here
因此,你可以从 Meteor.user()
获取经过身份验证的用户或 null,你应该将其封装在 useTracker
钩子中以使其具有响应性。然后你可以根据用户是否存在于会话中返回包含任务和其他所有内容的 Fragment
或 LoginForm
。
¥So you can get your authenticated user or null from Meteor.user()
, you should wrap it in a useTracker
hook for it to be reactive. Then you can return the Fragment
with Tasks and everything else or LoginForm
based on the user being present or not in the session.
js
import { Meteor } from 'meteor/meteor';
import React, { useState, Fragment } from 'react';
import { useTracker } from 'meteor/react-meteor-data';
import { TasksCollection } from '/imports/api/TasksCollection';
import { Task } from './Task';
import { TaskForm } from './TaskForm';
import { LoginForm } from './LoginForm';
..
export const App = () => {
const user = useTracker(() => Meteor.user());
..
return (
..
<div className="main">
{user ? (
<Fragment>
<TaskForm />
<div className="filter">
<button onClick={() => setHideCompleted(!hideCompleted)}>
{hideCompleted ? 'Show All' : 'Hide Completed'}
</button>
</div>
<ul className="tasks">
{tasks.map(task => (
<Task
key={task._id}
task={task}
onCheckboxClick={handleToggleChecked}
onDeleteClick={handleDelete}
/>
))}
</ul>
</Fragment>
) : (
<LoginForm />
)}
</div>
..
7.5:登录表单样式
¥7.5: Login Form style
好的,现在让我们设置登录表单的样式:
¥Ok, let's style the login form now:
将你的标签和输入对封装在 div
中,以便更容易在 CSS 上控制它。对按钮标签执行相同操作。
¥Wrap your pairs of label and input in div
s so it will easier to control it on CSS. Do the same to the button tag.
jsx
<form onSubmit={submit} className="login-form">
<div>
<label htmlFor="username">Username</label>
<input
type="text"
placeholder="Username"
name="username"
required
onChange={(e) => setUsername(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
placeholder="Password"
name="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div>
<button type="submit">Log In</button>
</div>
</form>
然后更新 CSS:
¥And then update the CSS:
css
.login-form {
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
align-items: center;
}
.login-form > div {
margin: 8px;
}
.login-form > div > label {
font-weight: bold;
}
.login-form > div > input {
flex-grow: 1;
box-sizing: border-box;
padding: 10px 6px;
background: transparent;
border: 1px solid #aaa;
width: 100%;
font-size: 1em;
margin-right: 16px;
margin-top: 4px;
}
.login-form > div > input:focus {
outline: 0;
}
.login-form > div > button {
background-color: #62807e;
}
现在,你的登录表单应该集中且美观。
¥Now your login form should be centralized and beautiful.
7.6:服务器启动
¥7.6: Server startup
从现在开始,每个任务都应该有一个所有者。因此,正如你之前所了解的那样,转到你的数据库,并从那里删除所有任务:
¥Every task should have an owner from now on. So go to your database, as you learn before, and remove all the tasks from there:
db.tasks.remove({});
更改你的 server/main.js
以使用你的 meteorite
用户作为所有者添加种子任务。
¥Change your server/main.js
to add the seed tasks using your meteorite
user as owner.
确保在此更改后重新启动服务器,以便 Meteor.startup
块再次运行。无论如何,这可能都会自动发生,因为你将在服务器端代码中进行更改。
¥Make sure you restart the server after this change so Meteor.startup
block will run again. This is probably going to happen automatically anyway as you are going to make changes in the server side code.
js
import { Meteor } from "meteor/meteor";
import { Accounts } from "meteor/accounts-base";
import { TasksCollection } from "/imports/api/TasksCollection";
const insertTask = (taskText, user) =>
TasksCollection.insert({
text: taskText,
userId: user._id,
createdAt: new Date(),
});
const SEED_USERNAME = "meteorite";
const SEED_PASSWORD = "password";
Meteor.startup(async () => {
if (!(await Accounts.findUserByUsername(SEED_USERNAME))) {
await Accounts.createUser({
username: SEED_USERNAME,
password: SEED_PASSWORD,
});
}
const user = await Accounts.findUserByUsername(SEED_USERNAME);
if ((await TasksCollection.find().countAsync()) === 0) {
[
"First Task",
"Second Task",
"Third Task",
"Fourth Task",
"Fifth Task",
"Sixth Task",
"Seventh Task",
].forEach((taskText) => insertTask(taskText, user));
}
});
看到我们正在将一个名为 userId
的新字段与用户 _id
字段一起使用,我们还设置了 createdAt
字段。
¥See that we are using a new field called userId
with our user _id
field, we are also setting createdAt
field.
7.7:任务所有者
¥7.7: Task owner
首先,让我们将发布更改为仅为当前登录的用户发布任务。这对于安全性很重要,因为你只发送属于该用户的数据。
¥First, let's change our publication to publish the tasks only for the currently logged user. This is important for security, as you send only data that belongs to that user.
js
Meteor.publish("tasks", function () {
const userId = this.userId;
if (!userId) {
return this.ready();
}
return TasksCollection.find({ userId });
});
现在让我们在尝试获取任何数据之前检查我们是否有 user
:
¥Now let's check if we have a user
before trying to fetch any data:
js
..
const tasks = useTracker(() => {
if (!user) {
return [];
}
return TasksCollection.find(
hideCompleted ? hideCompletedFilter : {},
{
sort: { createdAt: -1 },
}
).fetch();
});
const pendingTasksCount = useTracker(() => {
if (!user) {
return 0;
}
..
});
..
此外,更新 tasks.insert
方法以在创建新任务时包含字段 userId
:
¥Also, update the tasks.insert
method to include the field userId
when creating a new task:
js
..
Meteor.methods({
"tasks.insert"(doc) {
return TasksCollection.insertAsync({
...doc,
userId: this.userId,
});
},
..
7.8:注销
¥7.8: Log out
我们还可以通过在应用栏下方显示所有者的用户名来更好地组织我们的任务。你可以在我们的 Fragment
开始标记之后立即包含一个新的 div
。
¥We also can better organize our tasks by showing the username of the owner below our app bar. You can include a new div
right after our Fragment
start tag.
在此,你也可以添加一个 onClick
处理程序来注销用户。它非常简单,只需调用 Meteor.logout()
即可。
¥On this, you can add an onClick
handler to logout the user as well. It is very straightforward, just call Meteor.logout()
on it.
js
..
const logout = () => Meteor.logout();
return (
..
<Fragment>
<div className="user" onClick={logout}>
{user.username} 🚪
</div>
..
请记住也要为你的用户名设置样式。
¥Remember to style your username as well.
css
.user {
display: flex;
align-self: flex-end;
margin: 8px 16px 0;
font-weight: bold;
cursor: pointer;
}
呼!你在此步骤中做了很多工作。对用户进行身份验证,在任务中设置用户,并为用户提供注销方式。
¥Phew! You have done quite a lot in this step. Authenticated the user, set the user in the tasks, and provided a way for the user to log out.
你的应用应如下所示:
¥Your app should look like this:
在下一步中,我们将学习如何部署你的应用!
¥In the next step, we are going to learn how to deploy your app!
8:部署
¥8: Deploying
部署 Meteor 应用类似于部署任何其他使用 websockets 的 Node.js 应用。你可以在 我们的指南 中找到部署选项,包括 Meteor Up、Docker 和我们推荐的方法 Galaxy。
¥Deploying a Meteor application is similar to deploying any other Node.js app that uses websockets. You can find deployment options in our guide, including Meteor Up, Docker, and our recommended method, Galaxy.
在本教程中,我们将在 Galaxy 上部署我们的应用,这是我们自己的云解决方案。Galaxy 提供免费计划,因此你可以部署和测试你的应用。很酷,对吧?
¥In this tutorial, we will deploy our app on Galaxy, which is our own cloud solution. Galaxy offers a free plan, so you can deploy and test your app. Pretty cool, right?
8.1:创建你的账户
¥8.1: Create your account
你需要一个 Meteor 账户来部署你的应用。如果你还没有,可以使用 在此处注册。使用此账户,你可以访问我们的包管理器、Atmosphere、论坛 等。
¥You need a Meteor account to deploy your apps. If you don’t have one yet, you can sign up here. With this account, you can access our package manager, Atmosphere, Forums and more.
8.2:设置 MongoDB(可选)
¥8.2: Set up MongoDB (Optional)
由于你的应用使用 MongoDB,第一步是设置 MongoDB 数据库,Galaxy 提供免费计划的 MongoDB 托管以供测试,你还可以请求允许你扩展的生产就绪数据库。
¥As your app uses MongoDB the first step is to set up a MongoDB database, Galaxy offers MongoDB hosting on a free plan for testing purposes, and you can also request for a production ready database that allows you to scale.
在任何 MongoDB 提供程序中,你都会有一个必须使用的 MongoDB URL。如果你使用 Galaxy 提供的免费选项,则初始设置已为你完成。
¥In any MongoDB provider you will have a MongoDB URL which you must use it. If you use the free option provided by Galaxy, the initial setup is done for you.
Galaxy MongoDB URL 将如下所示:mongodb://username:<password>@org-dbname-01.mongodb.galaxy-cloud.io
.
¥Galaxy MongoDB URL will be like this: mongodb://username:<password>@org-dbname-01.mongodb.galaxy-cloud.io
.
你可以阅读有关 Galaxy MongoDB 此处 和常规 MongoDB 设置 此处 的更多信息。
¥You can read more about Galaxy MongoDB here and general MongoDB set up here.
8.3:设置设置
¥8.3: Set up settings
你需要创建一个设置文件,它是一个 JSON 文件,Meteor 应用可以从中读取配置。在项目根目录中名为 private
的新文件夹中创建此文件。需要注意的是,private
是一个特殊文件夹,不会发布到应用的客户端。
¥You need to create a setting file, it’s a JSON file that Meteor apps can read configurations from. Create this file in a new folder called private
in the root of your project. It is important to notice that private
is a special folder that is not going to be published to the client side of your app.
确保将 Your MongoDB URL
替换为你自己的 MongoDB URL 😃
¥Make sure you replace Your MongoDB URL
by your own MongoDB URL 😃
json
{
"galaxy.meteor.com": {
"env": {
"MONGO_URL": "Your MongoDB URL"
}
}
}
8.4:部署它
¥8.4: Deploy it
现在你已准备好部署,请在部署之前运行 meteor npm install
以确保安装了所有依赖。
¥Now you are ready to deploy, run meteor npm install
before deploying to make sure all your dependencies are installed.
你还需要选择一个子域来发布你的应用。我们将使用免费的主域 meteorapp.com
,它包含在任何 Galaxy 计划中。
¥You also need to choose a subdomain to publish your app. We are going to use the main domain meteorapp.com
that is free and included on any Galaxy plan.
在此示例中,我们将使用 react-meteor-3.meteorapp.com
,但请确保你选择其他 XX,否则你将收到错误。
¥In this example we are going to use react-meteor-3.meteorapp.com
but make sure you select a different one, otherwise you are going to receive an error.
你可以了解如何在 Galaxy 此处 上使用自定义域。从 Essentials 计划开始,可以使用自定义域。
¥You can learn how to use custom domains on Galaxy here. Custom domains are available starting with the Essentials plan.
运行部署命令:
¥Run the deployment command:
shell
meteor deploy react-meteor-3.meteorapp.com --free --mongo
如果你没有在 Galaxy 上使用 MongoDB 的免费托管,请从部署脚本中删除
--mongo
标志,并使用适合你应用的设置添加--settings private/settings.json
。¥If you are not using the free hosting with MongoDB on Galaxy, then remove the
--mongo
flag from the deploy script and add--settings private/settings.json
with the proper setting for your app.
确保将 react-meteor-3
替换为你想要用作子域的自定义名称。你将看到如下日志:
¥Make sure you replace react-meteor-3
by a custom name that you want as subdomain. You will see a log like this:
shell
meteor deploy react-meteor-3.meteorapp.com --settings private/settings.json
Talking to Galaxy servers at https://us-east-1.galaxy-deploy.meteor.com
Preparing to build your app...
Preparing to upload your app...
Uploaded app bundle for new app at vue-tutorial.meteorapp.com.
Galaxy is building the app into a native image.
Waiting for deployment updates from Galaxy...
Building app image...
Deploying app...
You have successfully deployed the first version of your app.
For details, visit https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com
此过程通常只需几分钟,但这取决于你的互联网速度,因为它会将你的应用包发送到 Galaxy 服务器。
¥This process usually takes just a few minutes, but it depends on your internet speed as it’s going to send your app bundle to Galaxy servers.
Galaxy 构建一个包含你的应用包的新 Docker 映像,然后使用它部署容器,阅读更多。你可以查看 Galaxy 上的日志,包括 Galaxy 构建 Docker 映像并部署它的部分。
¥Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, read more. You can check your logs on Galaxy, including the part that Galaxy is building your Docker image and deploying it.
8.5:访问应用并享受
¥8.5: Access the app and enjoy
现在,你应该能够在 https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com
访问你的 Galaxy 仪表板。
¥Now you should be able to access your Galaxy dashboard at https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com
.
你还可以在 Galaxy 2.0 上访问你的应用,该应用目前处于 https://galaxy-beta.meteor.com/<your-username>/us-east-1/apps/<your-app-name>.meteorapp.com
测试阶段。请记住使用你自己的子域而不是 react-meteor-3
。
¥You can also access your app on Galaxy 2.0 which is currently in beta at https://galaxy-beta.meteor.com/<your-username>/us-east-1/apps/<your-app-name>.meteorapp.com
. Remember to use your own subdomain instead of react-meteor-3
.
你可以在 react-meteor-3.meteorapp.com 访问应用!只需使用你的子域即可访问你的子域!
¥You can access the app at react-meteor-3.meteorapp.com! Just use your subdomain to access yours!
我们部署到在美国(us-east-1)运行的 Galaxy,我们也在世界其他地区运行 Galaxy,请查看列表 此处。这很重要,你的应用在 Galaxy 上运行,可供世界上任何人使用!
¥We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list here. This is huge, you have your app running on Galaxy, ready to be used by anyone in the world!
9:后续步骤
¥9: Next Steps
你已完成本教程!
¥You have completed the tutorial!
现在,你应该对使用 Meteor 和 Vue 有了很好的理解。
¥By now, you should have a good understanding of working with Meteor and Vue.
信息
你可以在我们的 GitHub 存储库 中找到此应用的最终版本。
¥You can find the final version of this app in our GitHub repository.
以下是你接下来可以执行的一些选项:
¥Here are some options for what you can do next:
查看完整的 documentation 以了解有关 Meteor 3 的更多信息。
¥Check out the complete documentation to learn more about Meteor 3.
阅读 Galaxy 指南 以了解有关部署应用的更多信息。
¥Read the Galaxy Guide to learn more about deploying your app.
加入我们的 Meteor 论坛 和 Meteor Lounge Discord 社区,提出问题并分享你的经验。
¥Join our community on the Meteor Forums and the Meteor Lounge on Discord to ask questions and share your experiences.
我们迫不及待地想看看你接下来会构建什么!
¥We can't wait to see what you build next!