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: 0;
    box-sizing: border-box;
  }
  
  .material-icons {
    font-size: 24px;
    line-height: 1;
    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: 0;
  }
  
  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM