ASP.NET Core + Vue 小项目:构造自己的在线 Markdown 笔记本

目录

  • 概要
  • 知识点
  • 完整示例图
  • 代码与资源文件
  • 流程步骤

概要

基于 MVP 最小可行性产品设计理念,我们先完成一个可以使用,并具备基本功能的 Markdown 笔记本应用,再进行逐步完善。

知识点

本文运用了 Vue 的计算属性、双向绑定、指令、生命周期钩子,还有 localStorage 和异步请求等知识点。

完整示例图

代码与资源文件

https://github.com/liqingwen2015/MarkdownDemo
为了避免网络原因造成的问题,文中所使用的第三方库以及 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.cnblogs.com/liqingwen/p/10264626.html
【参考】Vue.js 2 Web Development Projects

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容

  • 33、JS中的本地存储 把一些信息存储在当前浏览器指定域下的某一个地方(存储到物理硬盘中)1、不能跨浏览器传输:在...
    萌妹撒阅读 2,061评论 0 2
  • vue-cli搭建项目 确保安装了node与npm 再目标文件夹下打开终端 执行cnpm i vue-cli -g...
    Akiko_秋子阅读 3,209评论 1 22
  • 在尤大微博铺垫着“将会引入一些关于 TypeScript 的改进”一周之后,代号为 Level E 的 Vue.j...
    极乐叔阅读 14,686评论 0 12
  • ## 框架和库的区别?> 框架(framework):一套完整的软件设计架构和**解决方案**。> > 库(lib...
    Rui_bdad阅读 2,887评论 1 4
  • 活着,仅仅是活着。你梦想的样子可能与你实际的处境相差十万八千里,只是你心安而已,觉得这样的生活有意义,这样活...
    7d3dec75a0f5阅读 381评论 0 1