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. Netty4.x整合SpringBoot2.x使用Protobuf3详解

    前言 本篇文章主要介绍的是SpringBoot整合Netty以及使用Protobuf进行数据传输的相关内容.Protobuf会介绍下用法,至于Netty在netty 之 telnet HelloWor ...

  2. ssm框架搭建和整合流程

    Spring + SpringMVC + Mybatis整合流程 1      需求 1.1     客户列表查询 1.2     根据客户姓名模糊查询 2      整合思路 第一步:整合dao层 ...

  3. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

  4. 详解synchronized与Lock的区别与使用

    知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...

  5. Android:JNI与NDK(一)

    友情提示:欢迎关注本人公众号,那里有更好的阅读体验以及第一时间获取最新文章 本篇目录 以下举例代码均来自:NDK示例代码 一.前言 安卓开发中很多场景需要用到NDK来开发,比如,音视频的渲染,图像的底 ...

  6. c# 多种方法调整屏幕亮度

    Github: https://github.com/CHNMaxGor/AjustScreenBrightness 方法一: 使用网上常说的 Gdi32.dll 下的 SetDeviceGammaR ...

  7. 第一册:lesson1-2.

    原文: lesson 1 Excuse me! Excuse me! Yes? Is this your handbag? Pardon? Is this your handbag? Yes it i ...

  8. 设计模式 | 工厂方法模式(factory method)

    定义: 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 结构:(书中图,侵删) 一个工厂的抽象接口 若干个具体的工厂类 一个需要创建对象的抽象接口 若干个 ...

  9. 移动端布局方案汇总&&原理解析

    阿里flexible布局 - 版本1.x 该布局于 2017年8月9日被2.0版本取代 实现原理 假设(UI稿750px宽) 设置viewport的 scale = 1/window.devicePi ...

  10. arcgis api 4.x for js 结合 react 入门开发系列初探篇(附源码下载)

    你还在使用 JQuery 或者 Dojo 框架开发 arcgis api 4.x for js 吗?想试试模块化开发吗?随着前端技术的发展,arcgis api 4.x for js 也有了结合 re ...