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 笔记本应用的更多相关文章

  1. 跟我一起做一个vue的小项目(二)

    这个vue项目是紧跟着之前的项目跟我一起做一个vue的小项目(一)来的. 我继续后面的开发(写的比较粗糙,边学边记录) 下图是header头部的样式 header组件内容如下 //header.vue ...

  2. 跟我一起做一个vue的小项目(APPvue2.5完结篇)

    先放一下这个完结项目的整体效果 下面跟我我一起进行下面项目的进行吧~~~ 接下来我们进行的是实现header的渐隐渐显效果,并且点击返回要回到首页 我们先看效果 在处理详情页向下移动过程中,heade ...

  3. 跟我一起做一个vue的小项目(八)

    接下来我们进行的是城市选择页面的路由配置 添加city.vue,使其点击城市,然后跳转到city页面 //router.js import Vue from 'vue' import Router f ...

  4. 跟我一起做一个vue的小项目(七)

    先看下我们所做项目的效果 这些数据都是我们在data中定义的,不是从后端数据中请求的.那么 接下来我们使用axios渲染数据 npm install axios --save 每个组件里面的数据都不相 ...

  5. 跟我一起做一个vue的小项目(五)

    接下来我们要做的是热门推荐页面,我们写一个推荐组件 使用的方法也是前端data中的数据渲染到页面上面,这里对文字过长取省略号的方法不成功使用了一个小技巧 使用了min-width:0 我们来看完整的代 ...

  6. 跟我一起做一个vue的小项目(四)

    接下来我们进行的是轮播页面下面的导航页的开发 我们需要的是实现轮播页下面的图标,并且实现轮播效果 这个话,其实基本思路先是渲染出小图标,然后,我们要对页数进行判断,如果图标的个数展示的就是8个,那个这 ...

  7. 跟我一起做一个vue的小项目(三)

    接下来我们进行轮播的开发 安装插件,选用2.6.7的稳定版本 npm install vue-awesome-swiper@2.6.7 --save 根据其github上面的用法,我们在全局引用,在m ...

  8. 跟我一起做一个vue的小项目(一)

    项目架子 npm install --global vue-cli vue init webpack travel cd travel/ npm run dev 运行效果 添加home页及其路由,添加 ...

  9. 跟我一起做一个vue的小项目(十一)

    接下来我们进行的是详情页动态路由及banner布局 先看页面的效果 下面是代码部分 <template> <div> <div class="banner&qu ...

随机推荐

  1. mysql 使用Navicat Lite如何打开‘查询编辑器’,使用sql语句对表进行操作!

    今天第一次使用mysql,尽然连查询编辑器都找不到,研究了半天,询问了下大牛,才搞出来,准备写下来,后面方面忘记了有查找的地方,哈哈哈~~ 如何打开"查询编辑器",使用sql语句进 ...

  2. Windows环境下消息中间件RabbitMq的搭建与应用

    前言 消息中间件目前已经在很多大型的项目上得到了运用,我们常见的有 RabbitMq, activitymq,kafka,rocketmq,其中rocketmq是阿里自己在kafka的基础上用java ...

  3. PHP Array 简介

    PHP Array 简介 数组函数允许您访问和操作数组. 支持单维和多维数组. 安装 数组函数属于 PHP 核心部分.无需安装即可使用这些函数. PHP 5 Array 函数 函数 描述 array( ...

  4. 有道云笔记MarkDown 插入图片

    前言: 在网上找了很多有道云笔记的markdown笔记如何插入本地图片,试了好几种方式都是一时可以显示而已,只要电脑重启或者换终端查看就无法显示图片了.网上常用的方法无非两种有效:github.博客. ...

  5. Python2与Python3字符编码的区别

    目录 字符编码应用之Python(掌握) 执行Python程序的三个阶段 Python2与Python3字符串类型的区别(了解) Python2 str类型 Unicode类型 Python3 字符编 ...

  6. Spring Boot 2.0 WebFlux 教程 (一) | 入门篇

    目录 一.什么是 Spring WebFlux 二.WebFlux 的优势&提升性能? 三.WebFlux 应用场景 四.选 WebFlux 还是 Spring MVC? 五.异同点 六.简单 ...

  7. MySQL 复制 - 性能与扩展性的基石 1:概述及其原理

    1. 复制概述 MySQL 内置的复制功能是构建基于 MySQL 的大规模.高性能应用的基础,复制解决的基本问题是让一台服务器的数据与其他服务器保持同步. 接下来,我们将从复制概述及原理.复制的配置. ...

  8. DSAPI 网页获取本地程序登陆用户

    这是一个非常简单的示例,在网页或其他平台获取程序中已经登陆的用户,当然也可以是其他信息. 源码 Imports DSAPI.网络.HTTP监听_DSWebAPI Public Class Form1 ...

  9. com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别

    com.mysql.jdbc.Driver 是 mysql-connector-java 5中的,com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6中的 ...

  10. Android-原笔迹手写的探索与开发

     前言 这篇文章主要是关于移动端原笔迹的开发,让平板上的手写效果达到笔迹光滑且有笔锋. 介绍关于原笔迹的算法思路. 项目github地址 算法思路分析 曲线拟合算法 利用曲线拟合算法增加虚拟的点,使得 ...