Series of articles:

  • Hand touch Hands Electron + Vue Combat tutorial (1)
  • Hand touch Hands Electron + Vue Combat tutorial (2)

In this article we will focus on the Markdown editing area on the right, the FileEdit component

Demand analysis

Screenshot of Youdao Cloud Note

As we can see from the screenshot, the right area is mainly composed of the title bar and content editing area, and the right side of the title bar also contains a row of operation buttons. We will ignore this row of buttons here, and consider iterative optimization after completing the development of the main functions.

Therefore, our final requirement is actually very simple, that is, “title bar + Markdown editing area = right area”. The title bar supports input modification, while the Markdown area supports editing operation and preview mode, as well as single-column and double-column switching, as well as the indispensable full-screen operation.

FileEdit component development

We will create a new component FileEdit under the component directory Components. The component is divided into two parts:

<! --* @description: File editing component * @Author: sufen
 * @Date: 2020-05-30 16:24:05
 * @LastEditTime: 2020-06-01 15:31:36  * @LastEditors: sufen  --> <template>  <div class="content-edit"> // Header area/ / edit area </div> </template>  <script> export default {  name: 'FileEdit' } </script>  <style lang="less" scoped></style> Copy the code

We use the Element el-Input component directly at the top of the component, and we need to modify the styling slightly:

<el-input class="file-title" v-model="currentTitle" placeholder="Please enter a title" />
Copy the code
  .file-title {
    padding-left: 5px;
    height: 56px;
    line-height: 56px;
    font-size: 18px;
 font-weight: 500;   /deep/ .el-input__inner {  height: inherit;  line-height: inherit;  font-weight: inherit;  border: none;  }  } Copy the code

mavon-editor

Markdown editing area, we can directly choose the third party plug-in packagemavon-edito3.9K Star is also a popular open source Markdown editorThe official documentation: https://github.com/hinesboy/mavonEditor.

I’m used to yarn, so you can use NPM as you like without any problems:

yarn add mavon-editor
Copy the code

After the installation, create a new file in plugin directory mavoneditor.js to introduce the dependency package:

/ ** @description: Markdown editor plug-in * @Author: sufen
 * @Date: 2020-05-30 16:31:31
 * @LastEditTime: 2020-06-02 11:01:31  * @LastEditors: sufen * / import Vue from 'vue' import mavonEditor from 'mavon-editor' import 'mavon-editor/dist/css/index.css'  Vue.use(mavonEditor) Copy the code

Adding the mavoneditor.js we just wrote to main.js completes the import:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/plugin/element-ui'
import '@/plugin/fortawesome' import '@/plugin/mavonEditor'  Vue.config.productionTip = false  new Vue({  router,  store,  render: h= > h(App) }).$mount('#app') Copy the code

Attrs and Listeners

Now we can add an editor to the component:

<mavon-editor v-bind="$attrs" v-on="$listeners" class="markdown-wrapper" />
Copy the code
.markdown-wrapper {
  height: calc(100vh - 56px);
Copy the code

&.fullscreen { height: 100vh; }}

$Listeners have used $attrs and $Listeners before, but they’re great tools for repackaging components and writing higher-order components. These two attributes are provided after VUE 2.4, which inevitably requires secondary encapsulation of some third-party components when we write business at ordinary times. For example, we now need to encapsulate a component with business features based on mavon-Editor, adding an el-Input input box to encapsulate some business logic.

In mavon – editor of the document, we can see components support twenty or thirty configuration parameters, we can pick a few appropriate parameter passing through props, but if one day someone else with your business components you think there is less parameters, it can only change your packaging components, or third-party components someday joined the new parameter, What should you do at this time?

Actually,FileEditComponents are based onmavon-editorWe’ve done some simple business wrapping and added a title entry box, which is just a component that acts as a middleman, passing the data around, so we can use it at this pointV-bind ="$attrs" : pass all attributes,v-on="$listeners"Pass all methods:

Finally, inHome.vueUse ours inFileEditComponents:

.sync

Vue 2.3.0+ : vue 2.3.0+ : Vue 2.3.0+ : Vue 2.3.0+ : Vue 2.3.0+ : Vue 2.3.0+ : Vue 2.3.0+ We can see the first official document: https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6

“In some cases, we may need to ‘bidirectional bind’ a prop. Unfortunately, true bidirectional binding creates maintenance problems, because child components can change parent components and there is no obvious source of change in either parent or child.” Sample code:

<file-edit :title.sync="title"/>
Copy the code

Will be extended to:

<file-edit :title="title" @update:title="val => title = val"/>
Copy the code

When a child component needs to update the value of title, it needs to explicitly fire an update event:

this.$emit('update:title', newValue)
Copy the code

Unidirectional data flow

All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand.

Additionally, every time the parent component changes, all prop in the child component will be refreshed to the latest value. This means that you should not change a prop inside a child component. If you do, Vue will issue a warning in the browser console.

Above for vue official to the explanation of the one-way data flow you can in a detailed look at: https://cn.vuejs.org/v2/guide/components-props.html

Because of the one-way data flow, the title bar component el-Input in our component can’t directly pass the title value from the V-model binding props. We need to define a currentTitle in data to bind V-model =”currentTitle”.

Contact the above.syncWe also need to listen incurrentTitleTo update the value passed by the props in real timetitle:

The following is the complete code of our component and the home.vue page call component code:

<! --* @description: File editing component * @Author: sufen
 * @Date: 2020-05-30 16:24:05
 * @LastEditTime: 2020-06-02 12:00:49  * @LastEditors: sufen  --> <template>  <div class="content-edit">  <el-input class="file-title" v-model="currentTitle" placeholder="Please enter a title" />  <mavon-editor v-bind="$attrs" v-on="$listeners" class="markdown-wrapper" />  </div> </template>  <script> export default {  name: 'FileEdit'. props: {  title: String  },  data() {  return {  currentTitle: this.title  }  },  watch: {  currentTitle(newValue) {  this.$emit('update:title', newValue)  }  } } </script>  <style lang="less" scoped> .content-edit {  .file-title {  padding-left: 5px;  height: 56px;  line-height: 56px;  font-size: 18px;  font-weight: 500;   /deep/ .el-input__inner {  height: inherit;  line-height: inherit;  font-weight: inherit;  border: none;  }  }   .markdown-wrapper {  height: calc(100vh - 56px);   &.fullscreen {  height: 100vh;  }  } } </style> Copy the code
<template>
  <div class="app-wrapper">
    <div class="sidebar-container">
      <file-search v-model="searchTitle" />
      <file-list :fileList="fileList" />
 </div>  <div class="main-container">  <file-edit  v-model="fileItem.content"  :title.sync="fileItem.title"  :boxShadow="false"  :subfield="false"  :shortCut="false"  @change="onSubmit"  />  </div>  </div> </template>  <script> import FileSearch from '@/components/FileSearch' import FileList from '@/components/FileList' import FileEdit from '@/components/FileEdit'  export default {  name: 'Home'. components: { FileSearch, FileList, FileEdit },  data() {  return {  searchTitle: ' '. fileList: [  { id: 1.title: 'File name 1'.time: '2020-06-21' },  { id: 2.title: 'File name 2'.time: '2020-06-21' },  { id: 3.title: 'File name 3'.time: '2020-06-21' },  { id: 4.title: 'File name 4'.time: '2020-06-21' },  { id: 5.title: 'File name 5'.time: '2020-06-21' },  { id: 6.title: 'File name 6'.time: '2020-06-21' },  { id: 1.title: 'File name 1'.time: '2020-06-21' },  { id: 2.title: 'File name 2'.time: '2020-06-21' },  { id: 3.title: 'File name 3'.time: '2020-06-21' },  { id: 4.title: 'File name 4'.time: '2020-06-21' },  { id: 5.title: 'File name 5'.time: '2020-06-21' },  { id: 6.title: 'File name 6'.time: '2020-06-21' } ]. fileItem: {  title: 'Hand Touch Hands Electron + Vue Combat Tutorial (3)'. content: ' '  }  }  },  methods: {  onSubmit(value) {  console.log(value)  console.log(this.fileItem)  }  } } </script>  <style lang="less" scoped> .app-wrapper {  display: flex;  .sidebar-container {  width: 300px;  height: 100vh;  border-right: 1px solid #eaeefb;  }  .main-container {  flex: 1;  overflow: hidden;  } } </style> Copy the code