目錄
- 概要
- 知識(shí)點(diǎn)
- 完整示例圖
- 代碼與資源文件
- 流程步驟
概要
基于 MVP 最小可行性產(chǎn)品設(shè)計(jì)理念,我們先完成一個(gè)可以使用,并具備基本功能的 Markdown 筆記本應(yīng)用,再進(jìn)行逐步完善。
知識(shí)點(diǎn)
本文運(yùn)用了 Vue 的計(jì)算屬性、雙向綁定、指令、生命周期鉤子,還有 localStorage 和異步請(qǐng)求等知識(shí)點(diǎn)。
完整示例圖
代碼與資源文件
https://github.com/liqingwen2015/MarkdownDemo
為了避免網(wǎng)絡(luò)原因造成的問(wèn)題,文中所使用的第三方庫(kù)以及 css 文件都下載好并且已經(jīng)放入里面。
流程步驟
1.先構(gòu)建一個(gè)基本的 html 文件,并引入核心 js 庫(kù)。
這里需要引入的第三方庫(kù)為 vue.js、marked.js。
<html>
<head>
<title></title>
<!-- 引入樣式文件 -->
<link rel="stylesheet" href="index.css" />
</head>
<body>
<!-- 引入 js 庫(kù) -->
<script src="/lib/vue.js"></script>
<script src="/lib/marked.js"></script>
<!-- js 代碼 -->
<script src="index.js"></script>
</body>
</html>
因?yàn)榭紤]到項(xiàng)目主要?jiǎng)澐譃閮蓧K,左邊是書寫區(qū)域,右邊為預(yù)覽區(qū)域,<body> 塊代碼修改為:
<body>
<!-- 引入 js 庫(kù) -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script>
<div id="app">
<!-- 主區(qū)域:書寫 -->
<section class="main"></section>
<!-- 預(yù)覽區(qū)域 -->
<aside class="preview"></aside>
</div>
<!-- js 代碼 -->
<script src="index.js"></script>
</body>
修改 js 代碼:創(chuàng)建 Vue 實(shí)例,并將其掛載到 DOM 元素上。
new Vue({
el: '#app'
})
【備注】上面的掛載方式是比較常見(jiàn)的一種,我們也可以使用 app.$mount('#app') 進(jìn)行掛載。
2.接下來(lái)我們使用 Vue 的雙向綁定機(jī)制控制輸入的內(nèi)容和預(yù)覽的內(nèi)容。
修改 html:
<body>
<!-- 引入 js 庫(kù) -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script>
<div id="app">
<!-- 主區(qū)域:書寫 -->
<section class="main">
<textarea v-model="editor"></textarea>
</section>
<!-- 預(yù)覽區(qū)域 -->
<aside class="preview">
{{editor}}
</aside>
</div>
<!-- js 代碼 -->
<script src="index.js"></script>
</body>
修改 js,增加數(shù)據(jù)屬性:
new Vue({
el: '#app',
data() {
return {
editor: '編輯器'
}
}
})
現(xiàn)在,打開(kāi) index.html 頁(yè)面,在瀏覽器頁(yè)面中的左側(cè)進(jìn)行輸入就可以在預(yù)覽窗口中同步看到輸入后的情況。
3.接下來(lái),我們需要對(duì)輸入的內(nèi)容經(jīng)過(guò) Markdown 形式轉(zhuǎn)換,在這里,我們使用 Vue 的計(jì)算屬性來(lái)進(jìn)行優(yōu)化渲染 Markdown 的實(shí)時(shí)預(yù)覽
修改 js:
new Vue({
// 掛載
el: '#app',
// 數(shù)據(jù)
data() {
return {
editor: '編輯器'
}
},
// 計(jì)算屬性
computed: {
editorPreview() {
return marked(this.editor);
}
}
})
修改 <body>,使用 v-html 指令取代 {{ }},以這種方式來(lái)渲染 HTML 元素。
<body>
<!-- 引入 js 庫(kù) -->
<script src="lib/vue.js"></script>
<script src="lib/marked.js"></script>
<div id="app">
<!-- 主區(qū)域:書寫 -->
<section class="main">
<textarea v-model="editor"></textarea>
</section>
<!-- 預(yù)覽區(qū)域 -->
<aside class="preview" v-html="editorPreview"> </aside>
</div>
<!-- js 代碼 -->
<script src="index.js"></script>
</body>
4.保存內(nèi)容
目前,如果關(guān)閉了瀏覽器或者對(duì)頁(yè)面進(jìn)行了刷新,所有內(nèi)容都會(huì)丟失。所以,我們目前使用 localStorage
的方式進(jìn)行數(shù)據(jù)的保存操作。
現(xiàn)在產(chǎn)生了一個(gè)疑問(wèn):應(yīng)該什么時(shí)候進(jìn)行保存呢?
我們現(xiàn)在使用 Vue 的偵聽(tīng)器功能來(lái)對(duì)數(shù)據(jù)的改動(dòng)進(jìn)行保存操作,因?yàn)樗梢员O(jiān)聽(tīng)到 editor 的每一改動(dòng)操作,意思是每次輸入操作都會(huì)觸發(fā)偵聽(tīng)器里面的方法。
修改 js:
new Vue({
// 掛載
el: '#app',
// 數(shù)據(jù)
data() {
return {
editor: '編輯器'
}
},
// 計(jì)算屬性
computed: {
editorPreview() {
return marked(this.editor);
}
},
// 偵聽(tīng)器
watch: {
editor(val) {
localStorage.setItem('editor', this.editor);
}
}
})
那么現(xiàn)在又產(chǎn)生了新的疑問(wèn):應(yīng)該怎樣才能夠在每次進(jìn)入這個(gè)頁(yè)面時(shí)顯示之前保存的信息呢?
現(xiàn)在,我們通過(guò)利用 Vue 的生命周期鉤子(目前使用 created 鉤子)來(lái)進(jìn)行數(shù)據(jù)的讀取及恢復(fù)。
修改 js:
new Vue({
// 掛載
el: '#app',
// 數(shù)據(jù)
data() {
return {
editor: '編輯器',
key: {
editor: 'editor'
}
}
},
// 計(jì)算屬性
computed: {
editorPreview() {
return marked(this.editor);
}
},
// 偵聽(tīng)器
watch: {
editor(val) {
localStorage.setItem(this.key.editor, this.editor);
}
},
// 生命周期鉤子
created() {
this.editor = localStorage.getItem(this.key.editor) || '第一次使用 Markdown 筆記本';
}
})
【備注】在進(jìn)行修改 js 后,editor 屬性第一次加載的時(shí)候可能為 null,這會(huì)導(dǎo)致整個(gè)應(yīng)用出錯(cuò),所以這里采用了默認(rèn)值。
5.localStorage 畢竟不是永久保存的方式,這里我使用一種較為簡(jiǎn)單的方式,保存方法替換為異步請(qǐng)求到 WebApi 接口保存到數(shù)據(jù)庫(kù)的方式
修改 html,引入 axios 庫(kù):
<script src="lib/axios.min.js"></script>
同時(shí),修改 js,增加兩個(gè) Http 請(qǐng)求的方法,獲取和保存:
new Vue({
// 掛載
el: '#app',
// 數(shù)據(jù)
data() {
return {
editor: '',
key: {
editor: 'editor'
},
url: 'http://localhost:34473/api/markdown' // 需要替換成自己的 API 路徑
}
},
// 計(jì)算屬性
computed: {
editorPreview() {
return marked(this.editor);
}
},
// 偵聽(tīng)器
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 的內(nèi)容如下:
[Route("api/[controller]")]
[ApiController]
public class MarkdownController : ControllerBase
{
public static MarkdownViewModel MarkdownViewModel = new MarkdownViewModel()
{
Content = "我的第一個(gè) Markdown 應(yīng)用"
};
[HttpGet]
public ActionResult<string> Get()
{
return MarkdownViewModel.Content;
}
[HttpPost]
public void Save([FromBody] MarkdownViewModel vm)
{
MarkdownViewModel = vm;
}
}
視圖模型 MarkdownViewModel.cs 的內(nèi)容如下:
public class MarkdownViewModel
{
public string Content { get; set; }
}
【備注】需要自行進(jìn)行 WebApi 的跨域配置,演示時(shí)進(jìn)行了忽略配置
【備注】示例代碼可從 https://github.com/liqingwen2015/MarkdownDemo 下載
【切換閱讀方式】https://www.cnblogs.com/liqingwen/p/10264626.html
【參考】Vue.js 2 Web Development Projects