Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说手把手教你Electron + Vue实战教程(三)[亲测有效],希望能够帮助你!!!。
作者:Sufen
转发链接:https://mp.weixin.qq.com/s/RkKNMNEaV0Hrp8iQ9MaX0A
手把手教你Electron + Vue实战教程(一)
手把手教你Electron + Vue实战教程(二)
手把手教你Electron + Vue实战教程(三)本篇
手把手教你Electron + Vue实战教程(四)
❝
上一篇我们已经完成了左侧菜单栏的基本样式功能,这一篇我们就主要来开发右侧的Markdown编辑区域,即文件编辑组件FileEdit
❞
有道云笔记截图
我们从截图里可以看到,右侧区域主要是由标题栏和内容编辑区组成,其中标题栏的右侧还包含了一排操作按钮,在这里我们就先把这一排按钮忽略了,先把主要功能开发完毕再考虑迭代优化。
所以我们的最终需要实现的需求其实很简单,就是「标题栏 + Markdown编辑区 = 右侧区域」,标题栏支持修改输入,Markdown区支持编辑操作和预览模式,同时也支持单栏和双栏切换,当然还有必不可少的全屏操作。
我们先在组件目录components下新建组件FileEdit,组件分为上下两部分:
<!--
* @Description: 文件编辑组件
* @Author: sufen
* @Date: 2020-05-30 16:24:05
* @LastEditTime: 2020-06-01 15:31:36
* @LastEditors: sufen
-->
<template>
<div class="content-edit">
// 标题区域
// 编辑区
</div>
</template>
<script>export default {
name: 'FileEdit'
}</script>
<style lang="less" scoped></style>
组件的顶部直接使用element组件el-input,我们需要稍微修改一点样式:
<el-input class="file-title" v-model="currentTitle" placeholder="请输入标题" />
.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的编辑区,我们可以直接选用第三方的插件包mavon-edito,3.9k star也算是比较火的一个开源Markdown编辑器了,详细说明大家可以看一下官方文档:https://github.com/hinesboy/mavonEditor
这里先来安装引入一下mavon-editor,我习惯使用yarn,大家可以根据自己喜好使用npm也木有任何问题:
yarn add mavon-editor
安装完后在插件目录plugin里新建文件mavonEditor.js引入依赖包:
/*
* @Description: markdown 编辑器插件
* @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)
在main.js中引入我们刚编写好的mavonEditor.js就算完成全部引入了:
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')
现在我们可以在组件里引入编辑器了:
<mavon-editor v-bind="$attrs" v-on="$listeners" class="markdown-wrapper" />
.markdown-wrapper {
height: calc(100vh - 56px);
&.fullscreen {
height: 100vh;
}
}
不知道大家之前有没有使用过$attrs和$listeners,它绝对是二次封装组件、写高阶组件的神器。这两个属性是vue 2.4版本之后提供的,在我们平时写业务的时候免不了需要对一些第三方组件进行二次封装。比如我们现在就需要基于mavon-editor封装一个带有业务特性的组件,添加了el-input输入框,将一些业务逻辑封装在其中。
在mavon-editor的文档中我们可以看到组件支持二三十个配置参数,我们可以适当的挑选几个参数通过props来传递,但如果哪天别人用你的业务组件的时候觉得你的参数少了,那就只能改你封装的组件了,亦或是哪天第三方组件加入了新参数,这个时候你又该怎么办?
其实FileEdit组件就是基于mavon-editor做了一些简单的业务封装,加入了一个标题输入框,它只是一个充当中间人的组件,负责传递数据而已,那么这个时候我们可以使用v-bind="$attrs":传递所有属性、v-on="$listeners"传递所有方法:
最后在Home.vue中使用我们的FileEdit组件:
这个也是vue 2.3.0+之后新加的一个语法糖,平时在封装组件的时候很好用的一个语法糖,它的实现机制和v-model是一样的。我们可以先看下官方文档:https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6
“在有些情况下,我们可能需要对一个prop进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。”示例代码:
<file-edit :title.sync="title"/>
会被扩展为:
<file-edit :title="title" @update:title="val => title = val"/>
当子组件需要更新 title 的值时,它需要显式地触发一个更新事件:
this.$emit('update:title', newValue)
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
以上为 vue 官方的对于单向数据流的解释,大家可以在官网详细看看:https://cn.vuejs.org/v2/guide/components-props.html
因为单向数据流的原因,我们组件内的标题栏组件el-input不能直接v-model绑定 props 传递过来的title值,我们需要在 data 中定义一个currentTitle,用以绑定v-model="currentTitle"。
联系上文的.sync,我们还需要监听currentTitle的值,实时更新 props 传递过来的值title:
至此,我们的FileEdit组件就暂时告一段落了,这篇基本都是一些 vue 组件封装的小技巧,希望能够对大家有些许帮助,下面贴出我们组件的完整代码和 Home.vue 页面调用组件代码:
<!--
* @Description: 文件编辑组件
* @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="请输入标题" />
<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>
<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: '文件名 1', time: '2020-06-21' },
{ id: 2, title: '文件名 2', time: '2020-06-21' },
{ id: 3, title: '文件名 3', time: '2020-06-21' },
{ id: 4, title: '文件名 4', time: '2020-06-21' },
{ id: 5, title: '文件名 5', time: '2020-06-21' },
{ id: 6, title: '文件名 6', time: '2020-06-21' },
{ id: 1, title: '文件名 1', time: '2020-06-21' },
{ id: 2, title: '文件名 2', time: '2020-06-21' },
{ id: 3, title: '文件名 3', time: '2020-06-21' },
{ id: 4, title: '文件名 4', time: '2020-06-21' },
{ id: 5, title: '文件名 5', time: '2020-06-21' },
{ id: 6, title: '文件名 6', time: '2020-06-21' }
],
fileItem: {
title: '手摸手Electron + Vue实战教程(三)',
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>
《手把手教你Electron + Vue实战教程(二)》
《手把手教你Electron + Vue实战教程(一)》
《收集22种开源Vue模板和主题框架「干货」》
《如何写出优秀后台管理系统?11个经典模版拿去不谢「干货」》
《手把手教你实现一个Vue自定义指令懒加载》
《基于 Vue 和高德地图实现地图组件「实践」》
《一个由 Vue 作者尤雨溪开发的 web 开发工具—vite》
《是什么让我爱上了Vue.js》
《1.1万字深入细品Vue3.0源码响应式系统笔记「上」》
《1.1万字深入细品Vue3.0源码响应式系统笔记「下」》
《「实践」Vue 数据更新7 种情况汇总及延伸解决总结》
《尤大大细说Vue3 的诞生之路「译」》
《提高10倍打包速度工具Snowpack 2.0正式发布,再也不需要打包器》
《大厂Code Review总结Vue开发规范经验「值得学习」》
《Vue3 插件开发详解尝鲜版「值得收藏」》
《带你五步学会Vue SSR》
《记一次Vue3.0技术干货分享会》
《Vue 3.x 如何有惊无险地快速入门「进阶篇」》
《「干货」微信支付前后端流程整理(Vue+Node)》
《带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」》
《「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分》
《「干货」Vue+Element前端导入导出Excel》
《「实践」Deno bytes 模块全解析》
《细品pdf.js实践解决含水印、电子签章问题「Vue篇」》
《基于vue + element的后台管理系统解决方案》
《Vue仿蘑菇街商城项目(vue+koa+mongodb)》
《基于 electron-vue 开发的音乐播放器「实践」》
《「实践」Vue项目中标配编辑器插件Vue-Quill-Editor》
《基于 Vue 技术栈的微前端方案实践》
《消息队列助你成为高薪 Node.js 工程师》
《Node.js 中的 stream 模块详解》
《「干货」Deno TCP Echo Server 是怎么运行的?》
《「干货」了不起的 Deno 实战教程》
《「干货」通俗易懂的Deno 入门教程》
《Deno 正式发布,彻底弄明白和 node 的区别》
《「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台》
《「实践」深入对比 Vue 3.0 Composition API 和 React Hooks》
《前端网红框架的插件机制全梳理(axios、koa、redux、vuex)》
《深入Vue 必学高阶组件 HOC「进阶篇」》
《深入学习Vue的data、computed、watch来实现最精简响应式系统》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(一)》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(二)》
《教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」》
《2020前端就业Vue框架篇「实践」》
《详解Vue3中 router 带来了哪些变化?》
《Vue项目部署及性能优化指导篇「实践」》
《Vue高性能渲染大数据Tree组件「实践」》
《尤大大细品VuePress搭建技术网站与个人博客「实践」》
《10个Vue开发技巧「实践」》
《是什么导致尤大大选择放弃Webpack?【vite 原理解析】》
《带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】》
《带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】》
《实践Vue 3.0做JSX(TSX)风格的组件开发》
《一篇文章教你并列比较React.js和Vue.js的语法【实践】》
《手拉手带你开启Vue3世界的鬼斧神工【实践】》
《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》
《怎样为你的 Vue.js 单页应用提速》
《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》
《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》
《Vue真是太好了 壹万多字的Vue知识点 超详细!》
《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》
《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》
《手把手教你深入浅出vue-cli3升级vue-cli4的方法》
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》
《Vue3 尝鲜》
《总结Vue组件的通信》
《Vue 开源项目 TOP45》
《2020 年,Vue 受欢迎程度是否会超过 React?》
《尤雨溪:Vue 3.0的设计原则》
《使用vue实现HTML页面生成图片》
《实现全栈收银系统(Node+Vue)(上)》
《实现全栈收银系统(Node+Vue)(下)》
《vue引入原生高德地图》
《Vue合理配置WebSocket并实现群聊》
《多年vue项目实战经验汇总》
《vue之将echart封装为组件》
《基于 Vue 的两层吸顶踩坑总结》
《Vue插件总结【前端开发必备】》
《Vue 开发必须知道的 36 个技巧【近1W字】》
《构建大型 Vue.js 项目的10条建议》
《深入理解vue中的slot与slot-scope》
《手把手教你Vue解析pdf(base64)转图片【实践】》
《使用vue+node搭建前端异常监控系统》
《推荐 8 个漂亮的 vue.js 进度条组件》
《基于Vue实现拖拽升级(九宫格拖拽)》
《手摸手,带你用vue撸后台 系列二(登录权限篇)》
《手摸手,带你用vue撸后台 系列三(实战篇)》
《前端框架用vue还是react?清晰对比两者差异》
《Vue组件间通信几种方式,你用哪种?【实践】》
《浅析 React / Vue 跨端渲染原理与实现》
《10个Vue开发技巧助力成为更好的工程师》
《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》
《1W字长文+多图,带你了解vue的双向数据绑定源码实现》
《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》
《手把手教你D3.js 实现数据可视化极速上手到Vue应用》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》
《Vue3.0权限管理实现流程【实践】》
《后台管理系统,前端Vue根据角色动态设置菜单栏和路由》
作者:Sufen
转发链接:https://mp.weixin.qq.com/s/RkKNMNEaV0Hrp8iQ9MaX0A