一、前言
在开始编写代码之前,先介绍一下即将开发的应用,并明确要达成的目标:
-
该笔记本应用允许用户以博客标记语言来写笔记;
-
支持博客的实时预览;
-
用户可以添加任意多条笔记;
-
笔记可以在用户下次打开应用时重新加载出来。
现在我们将从一个非常简单的笔记本应用人手:只在左侧显示一个文本编辑器,在右侧显示Markdown 实时预览。然后,再将应用扩展为支持多条笔记的完整笔记本。
二、项目编写
1、首先先新建一个index.html文件,先建一个大致的框架。
<html>
<head>
<title>Notebook</title>
<!-- Icons & Stylesheets -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1 style="text-align: center;font-size: 60px;">我的驿站</h1><hr>
<!-- Include the library in the page -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/marked"></script>
<!-- Notebook app -->
<div id="notebook">
<!-- Main pane -->
<section class="main">
<textarea v-model="content"></textarea>
</section>
<!-- Preview pane -->
<aside class="preview" v-html="notePreview">
</aside>
</div>
<!-- Some JavaScript -->
<script src="script.js"></script>
</body>
</html>
2、新建一个script.js
new Vue({
name: 'notebook',
// CSS selector of the root DOM element
el: '#notebook',
// Some data
data () {
return {
// content: 'This is a note',
content: localStorage.getItem('content') || '你可以从此处开始 **编辑**',
}
},
// Computed properties
computed: {
notePreview () {
// Markdown rendered to HTML
return marked(this.content)
},
},
// Change watchers
watch: {
/*content: {
handler (val, oldVal) {
console.log('new note:', val, 'old note:', oldVal)
localStorage.setItem('content', val)
},
immediate: true,
},*/
/*content (val) {
localStorage.setItem('content', val)
},*/
/*content: {
handler: 'saveNote',
},*/
content: 'saveNote',
},
methods: {
saveNote (val, oldVal) {
console.log('new note:', val, 'old note:', oldVal)
console.log('saving note:', this.content)
localStorage.setItem('content', this.content)
this.reportOperation('saving')
},
reportOperation (opName) {
console.log('The', opName, 'operation was completed!')
},
},
/* created () {
this.content = localStorage.getItem('content') || 'You can write in **markdown**'
}, */
})
3、新建一个style.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;
}
#notebook > * {
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: #40b883;
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: #40b883;
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: #40b883;
}
h1,
h2,
h3 {
margin: 10px 0 4px;
color: #40b883;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.2em;
}
h4 {
font-size: 1.1em;
font-weight: normal;
}