Vue + WebApi 小项目:构造自己的在线 Markdown 笔记本应用
Vue + WebApi 小项目:构造自己的在线 Markdown 笔记本应用
目录
- 概要
- 知识点
- 完整示例图
- 代码与资源文件
- 流程步骤
概要
基于 MVP 最小可行性产品设计理念,我们先完成一个可以使用,并具备基本功能的 Markdown 笔记本应用,再进行逐步完善。
知识点
本文会指导初学者如何一步步运用 Vue 的计算属性、双向绑定、指令、生命周期钩子,还有 localStorage 和异步请求等知识点。
完整示例图

代码与资源文件
https://github.com/liqingwen2015/MarkdownDemo
为了避免网络原因造成的问题,文中所使用的第三方库(可自己去官方下载最新版,文章使用的是当前发布时间最新版本的 js 文件)以及 css 文件都下载好并且已经放入里面。
body {
font-family: sans-serif;
font-size: 16px;
height: 100%;
margin:;
box-sizing: border-box;
} .material-icons {
font-size: 24px;
line-height:;
vertical-align: middle;
margin: -3px;
padding-bottom: 1px;
} #app > * {
float: left;
display: flex;
flex-direction: column;
height: 100%; > * {
flex: auto 0 0;
}
} .side-bar {
background: #f8f8f8;
width: 20%;
box-sizing: border-box;
} .note {
padding: 16px;
cursor: pointer;
} .note:hover {
background: #ade2ca;
} .note .icon {
float: right;
} button,
input,
textarea {
font-family: inherit;
font-size: inherit;
line-height: inherit;
box-sizing: border-box;
outline: none !important;
} button,
.note.selected {
background: orange;
color: white;
} button {
border-radius: 3px;
border: none;
display: inline-block;
padding: 8px 12px;
cursor: pointer;
} button:hover {
background: #63c89b;
} input {
border: solid 2px #ade2ca;
border-radius: 3px;
padding: 6px 10px;
background: #f0f9f5;
color: #666;
} input:focus {
border-color: orange;
background: white;
color: black;
} button,
input {
height: 34px;
} .main, .preview {
width: 40%;
} .toolbar {
padding: 4px;
box-sizing: border-box;
} .status-bar {
color: #999;
font-style: italic;
} textarea {
resize: none;
border: none;
box-sizing: border-box;
margin: 0 4px;
font-family: monospace;
} textarea, .notes, .preview {
flex: auto 1 1;
overflow: auto;
} .preview {
padding: 12px;
box-sizing: border-box;
border-left: solid 4px #f8f8f8;
} .preview p:first-child {
margin-top:;
} a {
color: orange;
} h1,
h2,
h3 {
margin: 10px 0 4px;
color: orange;
} h1 {
font-size: 2em;
} h2 {
font-size: 1.5em;
} h3 {
font-size: 1.2em;
} h4 {
font-size: 1.1em;
font-weight: normal;
}
使用的 index.css 文件
流程步骤
1.先构建一个基本的 html 文件,并引入核心 js 库。
这里需要引入的第三方库为 vue.js、marked.js。
<html> <head>
<title></title>
<!-- 引入样式文件 -->
<link rel="stylesheet" href="index.css" />
</head> <body>
<!-- 引入 js 库 -->
<script src="/lib/vue.js"></script>
<script src="/lib/marked.js"></script> <!-- js 代码 -->
<script src="index.js"></script>
</body> </html>
因为考虑到项目主要划分为两块,左边是书写区域,右边为预览区域,<body> 块代码修改为:
<body>
<!-- 引入 js 库 -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script> <div id="app">
<!-- 主区域:书写 -->
<section class="main"></section> <!-- 预览区域 -->
<aside class="preview"></aside>
</div> <!-- js 代码 -->
<script src="index.js"></script>
</body>
修改 js 代码:创建 Vue 实例,并将其挂载到 DOM 元素上。
new Vue({
el: '#app'
})
【备注】上面的挂载方式是比较常见的一种,我们也可以使用 app.$mount('#app') 进行挂载。
2.接下来我们使用 Vue 的双向绑定机制控制输入的内容和预览的内容。
修改 html:
<body>
<!-- 引入 js 库 -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script> <div id="app">
<!-- 主区域:书写 -->
<section class="main">
<textarea v-model="editor"></textarea>
</section> <!-- 预览区域 -->
<aside class="preview">
{{editor}}
</aside>
</div> <!-- js 代码 -->
<script src="index.js"></script>
</body>
修改 js,增加数据属性:
new Vue({
el: '#app',
data() {
return {
editor: '编辑器'
}
}
})
现在,打开 index.html 页面,在浏览器页面中的左侧进行输入就可以在预览窗口中同步看到输入后的情况。
3.接下来,我们需要对输入的内容经过 Markdown 形式转换,在这里,我们使用 Vue 的计算属性来进行优化渲染 Markdown 的实时预览
修改 js:
new Vue({
// 挂载
el: '#app', // 数据
data() {
return {
editor: '编辑器'
}
}, // 计算属性
computed: {
editorPreview() {
return marked(this.editor);
}
}
})
修改 <body>,使用 v-html 指令取代 {{ }},以这种方式来渲染 HTML 元素。
<body>
<!-- 引入 js 库 -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script> <div id="app">
<!-- 主区域:书写 -->
<section class="main">
<textarea v-model="editor"></textarea>
</section> <!-- 预览区域 -->
<aside class="preview" v-html="editorPreview"> </aside>
</div> <!-- js 代码 -->
<script src="index.js"></script>
</body>

4.保存内容
目前,如果关闭了浏览器或者对页面进行了刷新,所有内容都会丢失。所以,我们目前使用 localStorage 的方式进行数据的保存操作。
现在产生了一个疑问:应该什么时候进行保存呢?
我们现在使用 Vue 的侦听器功能来对数据的改动进行保存操作,因为它可以监听到 editor 的每一改动操作,意思是每次输入操作都会触发侦听器里面的方法。
修改 js:
new Vue({
// 挂载
el: '#app', // 数据
data() {
return {
editor: '编辑器'
}
}, // 计算属性
computed: {
editorPreview() {
return marked(this.editor);
}
}, // 侦听器
watch: {
editor(val) {
localStorage.setItem('editor', this.editor);
}
}
})
那么现在又产生了新的疑问:应该怎样才能够在每次进入这个页面时显示之前保存的信息呢?
现在,我们通过利用 Vue 的生命周期钩子(目前使用 created 钩子)来进行数据的读取及恢复。
修改 js:
new Vue({
// 挂载
el: '#app', // 数据
data() {
return {
editor: '编辑器',
key: {
editor: 'editor'
}
}
}, // 计算属性
computed: {
editorPreview() {
return marked(this.editor);
}
}, // 侦听器
watch: {
editor(val) {
localStorage.setItem(this.key.editor, this.editor);
}
}, // 生命周期钩子
created() {
this.editor = localStorage.getItem(this.key.editor) || '第一次使用 Markdown 笔记本';
}
})
【备注】在进行修改 js 后,editor 属性第一次加载的时候可能为 null,这会导致整个应用出错,所以这里采用了默认值。
5.localStorage 毕竟不是永久保存的方式,这里我使用一种较为简单的方式,保存方法替换为异步请求到 WebApi 接口保存到数据库的方式
修改 html,引入 axios 库:
<script src="lib/axios.min.js"></script>
同时,修改 js,增加两个 Http 请求的方法,获取和保存:
new Vue({
// 挂载
el: '#app', // 数据
data() {
return {
editor: '',
key: {
editor: 'editor'
},
url: 'http://localhost:34473/api/markdown' // 需要替换成自己的 API 路径
}
}, // 计算属性
computed: {
editorPreview() {
return marked(this.editor);
}
}, // 侦听器
watch: {
editor(val) {
//localStorage.setItem(this.key.editor, this.editor);
this.save();
}
}, // 生命周期钩子
created() {
this.load();
// this.editor = localStorage.getItem(this.key.editor) || '第一次使用 Markdown 笔记本';
}, // 方法
methods: {
load() {
var that = this;
axios.get(that.url).then(function (result) {
console.log(result.data);
that.editor = result.data;
});
},
save() {
var that = this;
axios.post(that.url, { content: that.editor }).then(function (result) { });
}
}
})
新增的 API 控制器 MarkdownController.cs 的内容如下:
[Route("api/[controller]")]
[ApiController]
public class MarkdownController : ControllerBase
{
public static MarkdownViewModel MarkdownViewModel = new MarkdownViewModel()
{
Content = "我的第一个 Markdown 应用"
}; [HttpGet]
public ActionResult<string> Get()
{
return MarkdownViewModel.Content;
} [HttpPost]
public void Save([FromBody] MarkdownViewModel vm)
{
MarkdownViewModel = vm;
}
}
视图模型 MarkdownViewModel.cs 的内容如下:
public class MarkdownViewModel
{
public string Content { get; set; }
}
【备注】需要自行进行 WebApi 的跨域配置,演示时进行了忽略配置
【备注】示例代码可从 https://github.com/liqingwen2015/MarkdownDemo 下载
【切换阅读方式】https://www.jianshu.com/p/a17033ca91d9
【参考】Vue.js 2 Web Development Projects
Vue + WebApi 小项目:构造自己的在线 Markdown 笔记本应用的更多相关文章
- 跟我一起做一个vue的小项目(二)
这个vue项目是紧跟着之前的项目跟我一起做一个vue的小项目(一)来的. 我继续后面的开发(写的比较粗糙,边学边记录) 下图是header头部的样式 header组件内容如下 //header.vue ...
- 跟我一起做一个vue的小项目(APPvue2.5完结篇)
先放一下这个完结项目的整体效果 下面跟我我一起进行下面项目的进行吧~~~ 接下来我们进行的是实现header的渐隐渐显效果,并且点击返回要回到首页 我们先看效果 在处理详情页向下移动过程中,heade ...
- 跟我一起做一个vue的小项目(八)
接下来我们进行的是城市选择页面的路由配置 添加city.vue,使其点击城市,然后跳转到city页面 //router.js import Vue from 'vue' import Router f ...
- 跟我一起做一个vue的小项目(七)
先看下我们所做项目的效果 这些数据都是我们在data中定义的,不是从后端数据中请求的.那么 接下来我们使用axios渲染数据 npm install axios --save 每个组件里面的数据都不相 ...
- 跟我一起做一个vue的小项目(五)
接下来我们要做的是热门推荐页面,我们写一个推荐组件 使用的方法也是前端data中的数据渲染到页面上面,这里对文字过长取省略号的方法不成功使用了一个小技巧 使用了min-width:0 我们来看完整的代 ...
- 跟我一起做一个vue的小项目(四)
接下来我们进行的是轮播页面下面的导航页的开发 我们需要的是实现轮播页下面的图标,并且实现轮播效果 这个话,其实基本思路先是渲染出小图标,然后,我们要对页数进行判断,如果图标的个数展示的就是8个,那个这 ...
- 跟我一起做一个vue的小项目(三)
接下来我们进行轮播的开发 安装插件,选用2.6.7的稳定版本 npm install vue-awesome-swiper@2.6.7 --save 根据其github上面的用法,我们在全局引用,在m ...
- 跟我一起做一个vue的小项目(一)
项目架子 npm install --global vue-cli vue init webpack travel cd travel/ npm run dev 运行效果 添加home页及其路由,添加 ...
- 跟我一起做一个vue的小项目(十一)
接下来我们进行的是详情页动态路由及banner布局 先看页面的效果 下面是代码部分 <template> <div> <div class="banner&qu ...
随机推荐
- Oracle系列-锁表与解锁解决方案(大招版)-解决问题才是王道
[Oracle系列-锁表与解锁解决方案(大招版)] --1查看被锁的表 select b.owner,b.object_name,a.session_id,a.locked_mode from v$l ...
- 好几个div(元素)找到最后一个
<div> <div></div> <div></div> <div></div> </div> //找 ...
- Quartz定时调度在Web中的应用
1.在数据库中建一个job表和job日志表 job表
- Python中pathlib模块
Python中pathlib模块 Path.cwd():返回当前目录的路径 Path.home():返回当前用户的家目录 Path.stat():返回此路径信息 Path.touch():创建文件 P ...
- 我不是bug神(JVM问题排查)
Story background 回望2018年12月,这也许是程序员们日夜不得安宁的日子,皆因各种前线的系统使用者都需要冲业绩等原因,往往在这个时候会向系统同时写入海量的数据,当我们的应用或者数据库 ...
- handler原理
一.消息机制概述 1.消息机制的简介 (1)Handler是什么 handler使Android给我们提供的用来更新UI的一套机制,也是一套消息处理机制:我们可以用它发送处理消息. (2)Androi ...
- html5中的indexDB
1.关系型数据库和非关系型数据库 一致性: 事务完成时,必须让所有的数据具有一致的状态,例如要写入100个数据,前99个成功了,结果第100个不合法,此时事务会回滚到最初状态.这样保证事务结束和开始时 ...
- 通过改进团队流程最大限度发挥Scrum的优势
团队如何最大限度地发挥Scrum和敏捷的优势? 回想一下,Scrum团队在Scrum的框架内定义了自己的流程.这其中包括方法.工具和互动以及如何履行Scrum角色的职责.如何使用工件和事件等. 如何确 ...
- 使用 Premiere 制作视频简介
Premiere 简介 经常上B站或其他视频网站,有很多个人制作的有趣视频.也会想要自己制作视频.目前网上常见的视频剪辑软件有很多种,神剪辑.爱剪辑.会声会影.EDIUS等.但在专业视频剪辑师中,使用 ...
- redis增删查改数据Util
目录 (1)需要导入的包 (2)redis配置文件 (3)RedisUtil类 (1)需要导入的包 <dependency> <groupId>org.springframew ...