博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Node.js构建实时Markdown编辑器
阅读量:2511 次
发布时间:2019-05-11

本文共 21210 字,大约阅读时间需要 70 分钟。

介绍 (Intro)

Markdown is a popular text format written in an easy-to-read way and is convertible to HTML. It is a markup format that has been popularized by sites such as Github and Stack Overflow. Today we will be building an app that let's us view the raw markdown on the left side and the converted markdown (to HTML) on the right side. We will also allow multiple people to work on the same markdown document at the same time via a shareable URL and all changes will be saved.

Markdown是一种流行的文本格式,易于阅读,并且可以转换为HTML。 这是一种标记格式,已被Github和Stack Overflow等网站所普及。 今天,我们将构建一个应用程序,让我们在左侧查看原始markdown,在右侧查看转换的markdown(至HTML)。 我们还将允许多个人通过可共享的网址同时处理同一个Markdown文档,并且所有更改都将被保存。

设置节点 (Setup Node)

Let's get started on our real time markdown viewer app. We will be creating our backend in Node for this application. Create a project directory and then from the command line run the following:

让我们开始使用我们的实时降价查看器应用程序。 我们将在Node中为此应用程序创建后端。 创建一个项目目录,然后从命令行运行以下命令:

npm init

This will prompt us with several questions. You can fill in the prompts accordingly. This will create a package.json file. Here is my sample package.json file.

这将提示我们几个问题。 您可以相应地填写提示。 这将创建一个package.json文件。 这是我的示例package.json文件。

{      "name": "RealtimeMarkdownViewer",      "description": "Realtime Markdown Viewer",      "main": "server.js",      "version": "1.0.0",      "repository": {        "type": "git",        "url": "git@github.com:sifxtreme/realtime-markdown.git"      },      "keywords": [        "markdown",        "realtime",        "sharejs"      ],      "author": "Asif Ahmed",      "dependencies": {        "express": "^4.12.4",        "ejs": "^2.3.1",        "redis": "^0.10.3",        "share": "0.6.3"      },      "engines": {        "node": "0.10.x",        "npm": "1.3.x"      }    }

Now let's create a server.js file in our root directory. This will be the main server file. We will be using as our web application framework. Using Express makes building a server simpler. With Express we will be using for our view templates. To install Express and EJS run the following commands:

现在,让我们在根目录中创建一个server.js文件。 这将是主服务器文件。 我们将使用作为我们的Web应用程序框架。 使用Express可以简化服务器的构建。 使用Express,我们将使用作为视图模板。 要安装Express和EJS,请运行以下命令:

node install --save expressnode install --save ejs

Also create a views folder and a public folder in the root directory.. The views folder is where we will be putting our EJS templates and the public folder is where will be serving our assets (stylesheets, javascript files, images). Now, we are ready to add some code to our server.js file.

还可以创建一个views文件夹和一个public的根目录文件夹。该views文件夹是我们将会把我们的EJS模板和public文件夹是将服务于我们的资产(样式表,JavaScript文件,图像)。 现在,我们准备将一些代码添加到我们的server.js文件中。

// server.jsvar express = require('express');var app = express();// set the view engine to ejsapp.set('view engine', 'ejs');// public folder to store assetsapp.use(express.static(__dirname + '/public'));// routes for appapp.get('/', function(req, res) {  res.render('pad');});// listen on port 8000 (for localhost) or the port defined for herokuvar port = process.env.PORT || 8000;app.listen(port);

Here we require the Express module, set the rendering engine to EJS, and we have a route for our home page. We also set the public directory to be a static directory. Lastly we set the server to listen on port 8000. From our home route, we will be rendering a file called pad.ejs from the view directory. This is a sample views/pad.ejs file.

在这里,我们需要Express模块​​,将渲染引擎设置为EJS,并且我们的主页具有路由。 我们还将public目录设置为静态目录。 最后,我们将服务器设置为侦听端口8000。从本地路由开始,我们将从view目录中渲染一个名为pad.ejs的文件。 这是一个示例views/pad.ejs文件。

Realtime Markdown Viewer
Hello World!

For styling, we added . Let's start up our node server (node server.js) and go http://localhost:8000 in our web browser. You should see something like this:

对于样式,我们添加了 。 让我们启动节点服务器( node server.js ),并在Web浏览器中进入http://localhost:8000 。 您应该会看到以下内容:

设置视图,CSS和JS文件 (Setup View, CSS, and JS Files)

We don't want our view file to just say "Hello World!", so let's edit it. We want a text area on the left side where the user can add markdown text and we want an area on the right side where the user can see converted into HTML markdown. If a user edits the text area, we want the markdown area to updated automatically. Stylistically, we want both our textarea and converted markdown area to be 100% height.

我们不希望视图文件只说“ Hello World!”,所以让我们对其进行编辑。 我们希望用户在左侧的文本区域可以添加markdown文本,我们希望用户在右侧的区域能够看到转换为HTML markdown的文本。 如果用户编辑文本区域,我们希望降价区域自动更新。 从风格上讲,我们希望文本区域和转换后的markdown区域均为100%高度。

To convert text to HTML, we will be using a library called . Let's review our updated view file.

要将文本转换为HTML,我们将使用一个名为的库。 让我们查看更新的视图文件。

Realtime Markdown Viewer

In the view file, we added links to a CSS and a JS file. We also added the textarea (where we write the markdown) and markdown area (where we view the markdown). Notice that they have specific ID's — this will be useful for our javascript. Let's add some style now to public/style.css.

在视图文件中,我们添加了指向CSS和JS文件的链接。 我们还添加了textarea(我们在其中编写markdown)和markdown区域(在其中查看markdown)。 请注意,它们具有特定的ID,这对我们的javascript很有用。 现在让我们向public/style.css添加一些样式。

/* public/style.css */html, body, section, .full-height {    height: 100%;} #pad{    font-family: Menlo,Monaco,Consolas,"Courier New",monospace;    border: none;    overflow: auto;    outline: none;    resize: none;    -webkit-box-shadow: none;    -moz-box-shadow: none;    box-shadow: none;}#markdown {    overflow: auto;    border-left: 1px solid black;}

For our javascript file (public/script.js) we want to create a function that can convert the textarea text, convert this HTML, and place this HTML in our markdown area. We also want an event listener, which for any input change of the text area (keydown, cut, paste, etc...) it will run this converter function. Finally, we want this function to run initially on page load. Here is our public/script.js file.

对于我们的javascript文件( public/script.js ),我们想要创建一个函数,该函数可以转换textarea文本,转换此HTML并将此HTML放置在我们的markdown区域中。 我们还需要一个事件监听器,对于文本区域的任何输入更改(击键,剪切,粘贴等),它将运行此转换器功能。 最后,我们希望此功能最初在页面加载时运行。 这是我们的public/script.js文件。

/* public/script.js */window.onload = function() {    var converter = new showdown.Converter();    var pad = document.getElementById('pad');    var markdownArea = document.getElementById('markdown');       var convertTextAreaToMarkdown = function(){        var markdownText = pad.value;        html = converter.makeHtml(markdownText);        markdownArea.innerHTML = html;    };    pad.addEventListener('input', convertTextAreaToMarkdown);    convertTextAreaToMarkdown();};

At this point we should have a functional app that will let us edit and view markdown right away. If we go to the homepage and add some sample markdown, we should see something like this:

在这一点上,我们应该有一个功能强大的应用程序,它将使我们可以立即编辑和查看markdown。 如果我们转到主页并添加一些降价示例,则应该看到类似以下内容:

将ShareJS添加到后端 (Add ShareJS to Backend)

Although we have a working prototype where a user can work on a markdown document, we have to add the feature where multiple people can work on single markdown document. At this point, if multiple users go the home page, they can each work on their own markdown document and each change they make will only be viewable to them. Also if they end up refreshing the page, all their work will be lost. Therefore, we need to add a way for multiple users to edit the same markdown document and we also need a way to save changes.

尽管我们有一个工作原型,用户可以在其中处理降价文档,但我们必须添加功能,使多个人可以在一个降价文档上进行工作。 此时,如果有多个用户访问主页,则他们每个人都可以使用自己的降价文档,并且他们所做的每次更改都只能对他们可见。 同样,如果他们最后刷新页面,他们的所有工作也会丢失。 因此,我们需要为多个用户添加一种编辑同一个Markdown文档的方法,并且还需要一种保存更改的方法。

As soon a user types on a page, we want this change to be reflected for all users. We want this markdown app to be a real time updating app. Basically we are trying to add a "Google Document" type functionality where changes are seen automatically. This is not an easy problem to solve, however, there is a library that does the heavy lifting for us. is a library that implements real time communication. ShareJS has one dependency though — it requires . Redis is a fast data store and that is where we will be storing our markdown files. To download and install Redis, we can follow the . Once we install Redis, we need to add the node modules for sharejs and redis, and then we should restart Node. ShareJS allows us to save the markdown document as soon as any user make a change to it.

用户在页面上键入内容后,我们希望此更改反映给所有用户。 我们希望这个降价应用程序是实时更新的应用程序。 基本上,我们尝试添加一种“ Google文档”类型的功能,以便可以自动看到更改。 这不是一个容易解决的问题,但是,有一个库可以为我们完成繁重的工作。 是一个实现实时通信的库。 但是,ShareJS具有一个依赖项-它需要 。 Redis是一个快速的数据存储,这就是我们将存储Markdown文件的地方。 要下载并安装Redis,请遵循 。 安装Redis之后,我们需要为sharejsredis添加节点模块,然后重新启动Node。 ShareJS允许我们在任何用户对其进行更改后立即保存降价文档。

npm install --save share@0.6.3npm install --save redis

First let's add the ShareJS code to our server file.

首先,让我们将ShareJS代码添加到我们的服务器文件中。

/* server.js */var express = require('express');var app = express();// set the view engine to ejsapp.set('view engine', 'ejs');// public folder to store assetsapp.use(express.static(__dirname + '/public'));// routes for appapp.get('/', function(req, res) {  res.render('pad');});app.get('/(:id)', function(req, res) {  res.render('pad');});// get sharejs dependenciesvar sharejs = require('share');require('redis');// options for sharejs var options = {  db: {type: 'redis'},};// attach the express server to sharejssharejs.server.attach(app, options);// listen on port 8000 (for localhost) or the port defined for herokuvar port = process.env.PORT || 8000;app.listen(port);

We require ShareJS and Redis, and set some options for it. We force ShareJS to use Redis as its data store. Then, we attach our Express server to our ShareJS object. Now we need to add links to some "ShareJS" frontend javascript files in our view file.

我们需要ShareJS和Redis,并为其设置一些选项。 我们强制ShareJS使用Redis作为其数据存储。 然后,我们将Express服务器附加到我们的ShareJS对象。 现在,我们需要在视图文件中添加一些“ ShareJS”前端javascript文件的链接。

Realtime Markdown Viewer

The files we require are for creating a socket connection to our backend (bcsocket.js) and sending and receiving textarea events (share.uncompressed.js and textarea.js). Finally, we need to actually add the code that implements ShareJS in our frontend javascript file (public/script.js).

我们需要的文件是用于创建到后端的套接字连接( bcsocket.js )以及发送和接收textarea事件( share.uncompressed.jstextarea.js )。 最后,我们需要在前端javascript文件( public/script.js )中实际添加实现ShareJS的代码。

/* public/script.js */window.onload = function() {    var converter = new showdown.Converter();    var pad = document.getElementById('pad');    var markdownArea = document.getElementById('markdown');       var convertTextAreaToMarkdown = function(){        var markdownText = pad.value;        html = converter.makeHtml(markdownText);        markdownArea.innerHTML = html;    };    pad.addEventListener('input', convertTextAreaToMarkdown);    sharejs.open('home', 'text', function(error, doc) {        doc.attach_textarea(pad);    });};

At the very bottom of this file, we open up a sharejs connection to "home" (because we are on the home page). We then attach the textarea to the object returned by this connection. This code keeps our textarea in sync with everyone else's textarea. So if Person A makes a change in their textarea, Person B will see that change automatically in their textarea. However, Person B's markdown area will not be updated right away. In fact, Person B's markdown area won't be updated until they make a change to their textarea themselves. This is a problem. We will solve this by making sure a change is reflected every second if the textarea has changed.

在此文件的最底部,我们打开到“ home”的sharejs连接(因为我们在主页上)。 然后,我们将文本区域附加到此连接返回的对象上。 此代码使我们的文本区域与其他人的文本区域保持同步。 因此,如果人员A在其文本区域中进行了更改,则人员B将在其文本区域中自动看到该更改。 但是,人员B的降价区域不会立即更新。 实际上,在B人自己更改文本区域之前,不会更新B人的降价区域。 这是个问题。 如果文本区域已更改,我们将确保每秒更改一次,以解决此问题。

/* public/script.js */window.onload = function() {    var converter = new showdown.Converter();    var pad = document.getElementById('pad');    var markdownArea = document.getElementById('markdown');       var previousMarkdownValue;              var convertTextAreaToMarkdown = function(){        var markdownText = pad.value;        previousMarkdownValue = markdownText;        html = converter.makeHtml(markdownText);        markdownArea.innerHTML = html;    };    var didChangeOccur = function(){        if(previousMarkdownValue != pad.value){            return true;        }        return false;    };    setInterval(function(){        if(didChangeOccur()){            convertTextAreaToMarkdown();        }    }, 1000);    pad.addEventListener('input', convertTextAreaToMarkdown);    sharejs.open('home', 'text', function(error, doc) {        doc.attach_textarea(pad);        convertTextAreaToMarkdown();    });};

多个Markdown文件 (Multiple Markdown Files)

Now we have an app where multiple people can edit the home page markdown file. However, what if we wanted to edit multiple markdown files. What if we wanted to go to the URL like http://localhost:3000/important_doc1 and collaborate with Bob and wanted to go to http://localhost:3000/important_doc2 and collaborate with Alice? How would we go about implementing this? First we want to add routes to match all wildcard routes in our server file.

现在,我们有了一个应用程序,多个人可以在其中编辑主页markdown文件。 但是,如果我们要编辑多个markdown文件,该怎么办。 如果我们想转到类似http://localhost:3000/important_doc1的URL并与Bob协作并且想转到http://localhost:3000/important_doc2并与Alice协作怎么办? 我们将如何实施呢? 首先,我们要添加路由以匹配服务器文件中的所有通配符路由。

/* server.js */var express = require('express');var app = express();// set the view engine to ejsapp.set('view engine', 'ejs');// public folder to store assetsapp.use(express.static(__dirname + '/public'));// routes for appapp.get('/', function(req, res) {  res.render('pad');});app.get('/(:id)', function(req, res) {  res.render('pad');});// get sharejs dependenciesvar sharejs = require('share');require('redis');// options for sharejs var options = {  db: {type: 'redis'},};// attach the express server to sharejssharejs.server.attach(app, options);// listen on port 8000 (for localhost) or the port defined for herokuvar port = process.env.PORT || 8000;app.listen(port);

On the frontend, instead of just connecting to "home", we want to use the correct sharejs room. Change "home" to document.location.pathname.

在前端,我们不仅仅是使用“ home”,而是要使用正确的sharejs室。 将"home"更改为document.location.pathname

/* public/script.js */sharejs.open(document.location.pathname, 'text', function(error, doc) {    doc.attach_textarea(pad);    convertTextAreaToMarkdown();});

清理 (Clean Up)

There are a couple issues that we need to address. One, it would be nice if the home page didn't just show random text that the last user entered. Let's disable the realtime markdown functionality for the home page.

我们需要解决几个问题。 一个,如果主页不只是显示最后一个用户输入的随机文本,那将是很好的。 让我们禁用主页的实时降价功能。

/* public/script.js */// ignore if on home pageif(document.location.pathname.length > 1){    // implement share js    var documentName = document.location.pathname.substring(1);    sharejs.open(documentName, 'text', function(error, doc) {        doc.attach_textarea(pad);        convertTextAreaToMarkdown();    });        }convertTextAreaToMarkdown();

The last issue we need to resolve is forcing our tab button to act as we would expect a tab button to act in our textarea. Currently if we press the tab button in our textarea, it will make us lose focus. This is terrible. Let's add a function for our textarea that fixes this tab issue. Below is a copy of our final front-end javascript file.

我们需要解决的最后一个问题是强制选项卡按钮起作用,就像我们期望选项卡按钮在文本区域中起作用一样。 当前,如果我们在文本区域中按下选项卡按钮,则会使我们失去焦点。 这真糟糕。 让我们为我们的textarea添加一个函数来解决此选项卡问题。 以下是我们最终的前端javascript文件的副本。

/* public/script.js */window.onload = function() {    var converter = new showdown.Converter();    var pad = document.getElementById('pad');    var markdownArea = document.getElementById('markdown');     // make the tab act like a tab    pad.addEventListener('keydown',function(e) {        if(e.keyCode === 9) { // tab was pressed            // get caret position/selection            var start = this.selectionStart;            var end = this.selectionEnd;            var target = e.target;            var value = target.value;            // set textarea value to: text before caret + tab + text after caret            target.value = value.substring(0, start)                            + "\t"                            + value.substring(end);            // put caret at right position again (add one for the tab)            this.selectionStart = this.selectionEnd = start + 1;            // prevent the focus lose            e.preventDefault();        }    });    var previousMarkdownValue;              // convert text area to markdown html    var convertTextAreaToMarkdown = function(){        var markdownText = pad.value;        previousMarkdownValue = markdownText;        html = converter.makeHtml(markdownText);        markdownArea.innerHTML = html;    };    var didChangeOccur = function(){        if(previousMarkdownValue != pad.value){            return true;        }        return false;    };    // check every second if the text area has changed    setInterval(function(){        if(didChangeOccur()){            convertTextAreaToMarkdown();        }    }, 1000);    // convert textarea on input change    pad.addEventListener('input', convertTextAreaToMarkdown);    // ignore if on home page    if(document.location.pathname.length > 1){        // implement share js        var documentName = document.location.pathname.substring(1);        sharejs.open(documentName, 'text', function(error, doc) {            doc.attach_textarea(pad);            convertTextAreaToMarkdown();        });            }    // convert on page load    convertTextAreaToMarkdown();};

推送到Heroku (Push to Heroku)

At this point, we have a fully functional realtime markdown editor. Now, how do we get it up and running on ? First, we need to make sure we have an account with Heroku. Then we will need to install the . In the command line, we will type heroku login to login to our heroku account. Heroku uses Git to push so we need to make sure we have created a repo and committed all our files to a local repo. To create a Heroku app, type heroku create from the command line. To use Heroku we will need to change how we configure Redis. We will using to add Redis to our Heroku app. From the command line type heroku addons:create redistogo. Also we will need to edit our server.js to handle this new configuration. Here is our final server.js file.

至此,我们有了一个功能齐全的实时降价编辑器。 现在,我们如何在上启动它并运行它? 首先,我们需要确保我们拥有Heroku帐户。 然后,我们将需要安装 。 在命令行中,我们将输入heroku login登录到我们的heroku帐户。 Heroku使用Git进行推送,因此我们需要确保已创建一个存储库并将所有文件提交到本地存储库。 要创建Heroku应用,请从命令行键入heroku create 。 要使用Heroku,我们将需要更改配置Redis的方式。 我们将使用将Redis添加到我们的Heroku应用中。 在命令行中输入heroku addons:create redistogo 。 另外,我们将需要编辑server.js以处理此新配置。 这是我们最终的server.js文件。

/* server.js */var express = require('express');var app = express();// set the view engine to ejsapp.set('view engine', 'ejs');// public folder to store assetsapp.use(express.static(__dirname + '/public'));// routes for appapp.get('/', function(req, res) {  res.render('pad');});app.get('/(:id)', function(req, res) {  res.render('pad');});// get sharejs dependenciesvar sharejs = require('share');// set up redis servervar redisClient;console.log(process.env.REDISTOGO_URL);if (process.env.REDISTOGO_URL) {  var rtg   = require("url").parse(process.env.REDISTOGO_URL);  redisClient = require("redis").createClient(rtg.port, rtg.hostname);  redisClient.auth(rtg.auth.split(":")[1]);} else {  redisClient = require("redis").createClient();}// options for sharejs var options = {  db: {type: 'redis', client: redisClient}};// attach the express server to sharejssharejs.server.attach(app, options);// listen on port 8000 (for localhost) or the port defined for herokuvar port = process.env.PORT || 8000;app.listen(port);

Lastly we need to tell our Heroku app that we are using Node and tell it which file Node uses to start up. Add a file called Procfile in the root directory.

最后,我们需要告诉Heroku应用程序我们正在使用Node,并告诉它Node使用哪个文件来启动。 在根目录中添加一个名为Procfile的文件。

web: node server.js

Now after we commit our changes into Git, we are ready to push our app to Heroku. Type git push heroku master to push our repo to Heroku. We should see Heroku returning a bunch of statuses as it is building the application. Type heroku open to open the app! (Note — we can always end up renaming our app. At first Heroku will probably give us a ridiculous sounding name). The first time may take a bit to load but it should be all working. Remember go to something like our_application_url/document to edit a new markdown document.

现在,在将更改提交到Git之后,我们准备将我们的应用程序推送到Heroku。 键入git push heroku master将我们的存储库推送到Heroku。 在构建应用程序时,我们应该看到Heroku返回一堆状态。 输入heroku open打开应用程序! (请注意-我们总是可以最终重命名我们的应用程序。起初,Heroku可能会给我们起一个可笑的名字)。 第一次加载可能需要一些时间,但应该可以正常工作。 记住,请访问诸如our_application_url/document以编辑新的降价文档。

Congratulations! We have a realtime markdown application that we can use for writing markdown and to collaborate with our friends.

恭喜你! 我们有一个实时Markdown应用程序,可用于编写Markdown并与我们的朋友合作。

You can see a working demo and view the entire code repo .

你可以看到一个工作演示和查看整个代码回购 。

翻译自:

转载地址:http://iiywd.baihongyu.com/

你可能感兴趣的文章
文件转码重写到其他文件
查看>>
AC自动机模板
查看>>
python 基本语法
查看>>
git配置
查看>>
【hexo】01安装
查看>>
使用case语句给字体改变颜色
查看>>
JAVA基础-多线程
查看>>
面试题5:字符串替换空格
查看>>
JSP九大内置对象及四个作用域
查看>>
ConnectionString 属性尚未初始化
查看>>
数据结构-栈 C和C++的实现
查看>>
MySQL基本命令和常用数据库对象
查看>>
poj 1222 EXTENDED LIGHTS OUT(位运算+枚举)
查看>>
进程和线程概念及原理
查看>>
Lucene、ES好文章
查看>>
android 生命周期
查看>>
jquery--this
查看>>
MySQL 5.1参考手册
查看>>
TensorFlow安装流程(GPU加速)
查看>>
OpenStack的容器服务体验
查看>>