electron-vue 創建桌面應用
為了學習 vue 並創建桌面應用,
我使用了 electron-vue 來進行搭建,
它集成了很多 vue 的工具,用來學習 vue 非常方便;
這里我用 IT Tools 這個項目來講解一下,
整個項目目前提供了四個模塊,分別是
- “正則表達式”
- “時間戳轉化”
- “顏色盒子”
- “Json轉化”
在這幾個模塊中,可以發現使用組件化的好處,處理多個組件之間各種數據變化非常方便!
這里我就以 “正則表達式” 頁面來說明:
創建 Electron-Vue 項目
克隆項目,從electron-vue 克隆項目,然后開始編寫代碼;
https://github.com/SimulatedGREG/electron-vue.git
通過"正則表達式"這個模塊,來了解 Vue 組件通信;
electron-vue 一開始已經為你生成一些文件頁面,我們可以按照他的方法創建我們自己的頁面;
創建路由:
src/renderer/router/index.js 文件中添加路由:
export default new Router({ routes: [ { path: '/', name: 'landing-page', component: require('@/components/LandingPage').default }, { path: '/regex-page', name: 'regex-page', component: require('@/components/RegexPage').default } ] });
這里我們的 url 為 /regex-page,並且require 了RegexPage組件,這個組件要放置在components 目錄下,所以我創建了文件:src/renderer/components/RegexPage.vue
編寫組件:
可以通過復制 LandingPage.vue 組件,將它改成新組件即可:
要實現這個頁面,頭部兩個輸入框,輸入后都能與下面的textarea 內容進行比較處理,得出結論;
這個用 組件化 vue 比純粹用js jquery 的 dom 操作要方便太多了;
通過 template 包裹寫成vue組件:
<template> <div id="regex-page">
<div class="regex-inner" v-show="currentTab === 'Home'">
<div class="regex-top">
<div class="regex-top-label">
<label>Your regular expression:</label>
</div>
<div class="regex-top-fields">
<div class="regex-diagonal">/</div>
<div class="regex-diagnoal-input">
<input type="text" name="regex-exp" @input='execRegex' :value='regexExp' />
</div>
<div class="regex-diagonal">/</div>
<div>
<input type="text" name="regex-opt" @input="execRegex" :value="regexOpt" />
</div>
</div>
</div>
<div class="regex-bottom">
<div class="regex-content">
<label>Your test string: </label>
<textarea class="regex-textarea" name="regex-content" @input="execRegex" :value='regexCont'></textarea>
</div>
<div class="result-content result-init" v-if="regexResult['status'] == 0">
{{ regexResult['content'] }}
</div>
<div class="result-content result-match" v-if="regexResult['status'] == 1">
<div>
<div class="regex-match-btn">
<label>Match Result:</label>
<a href="javascript:void(0)" class="clean-fields" @click="cleanAllFields">Clean Fields</a>
</div>
<div class="result-item">
<span v-for="(cont, indx) in regexResult['matchedContext']" :class="indx%2 !== 0 ? 'match' : null">{{ cont }}</span>
</div>
</div>
<ul v-if="regexResult['content'].length > 0">
<label>Match Groups:</label>
<div class="match-groups">
<li v-for="(itemGroup, index) in regexResult['content']">
<div class="group-item">
<label>Match Group {{ index + 1 }}:</label>
<ul>
<li v-if="i !== 0" v-for="(item, i) in itemGroup">{{ i }}: {{ item }}</li>
</ul>
</div>
</li>
</div>
</ul>
</div>
<div class="result-content result-not-match" v-if="regexResult['status'] == -1">
{{ regexResult['content'] }}
</div>
</div>
</div>
</div> </template> <script> import { mapState, mapActions } from 'vuex' export default { name: 'regex-page', computed: { ...mapState('Regex', { regexExp: state => state.regexExp, regexOpt: state => state.regexOpt, regexCont: state => state.regexCont, regexResult: state => state.regexResult}) }, methods: { ...mapActions('Regex', [ 'setNav', 'cleanFields', 'regexMatch' ]), cleanAllFields () { this.cleanFields() }, execRegex (event) { this.regexMatch(event) }, updateNav (title, index) { this.setNav({ title: title, index: index }) } } } </script>
<style lang="scss" scoped>
* {
}
</style>
至於,輸入框之間的交互,我使用 vuex 來實現他們之間數據的傳遞;
const state = { regexExp: '', regexOpt: '', regexCont: '', regexResult: { status: 0, content: "Here's result." } } const mutations = { REGEX_MATCH (state, target) { if (target.name === 'regex-exp') { state.regexExp = target.value } if (target.name === 'regex-opt') { state.regexOpt = target.value } if (target.name === 'regex-content') { state.regexCont = target.value } ... } const actions = { cleanFields ({ commit }) { commit('CLEAN_FIELDS') }, regexMatch ({ commit }, payload) { commit('REGEX_MATCH', payload.target) } } export default { state, mutations, actions }
state 給默認狀態;
mutations 更改對應 state;
actions 用於寫異步來改變狀態或提交 mutations 的更改;
state 的方法被我寫在computed,這樣組件中可以使用;
在methods 方法中使用 mapActions,並定義其他方法來調用這些action;
二、main.js 加入 store 容器
import App from './App' import router from './router' import store from './store' if (!process.env.IS_WEB) Vue.use(require('vue-electron')) Vue.http = Vue.prototype.$http = axios Vue.config.productionTip = false new Vue({ components: { App }, router, store, template: '<App/>' }).$mount('#app')
三、組件中通過 computed 或 data 使用 State,通過 methods 觸發 Actions 方法
import { mapState, mapActions } from 'vuex' export default { name: 'regex-page', computed: { ...mapState('Regex', { regexExp: state => state.regexExp, regexOpt: state => state.regexOpt, regexCont: state => state.regexCont, regexResult: state => state.regexResult}) }, methods: { ...mapActions('Regex', [ 'setNav', 'cleanFields', 'regexMatch' ]), cleanAllFields () { this.cleanFields() }, execRegex (event) { this.regexMatch(event) }, updateNav (title, index) { this.setNav({ title: title, index: index }) } } }
在組件文件中引用了
mapState, mapActions
方法,他可以獲取這個 store 里的 state 和 action 方法,
不過要注意命名空間的使用,此處使用了Regex作為命名空間,所以要在mapState 和 mapActions 中加 命名空間;
命名空間定義文件在:src/renderer/store/modules/index.js 文件;
const files = require.context('.', false, /\.js$/)
const modules = {} files.keys().forEach(key => { if (key === './index.js') return modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default modules[key.replace(/(\.\/|\.js)/g, '')]['namespaced'] = true }) export default modules
但是直接 (‘Regex’, [regexExp: state => state.regexExp]) 是無法使用的,必須在 module 中聲明 namespaced: true 才可以;
…mapActions() 是將里面的對象 扁平化 到 外面的對象中;
直接 mapActions 只是打開了方法,還未執行:
刪除 createSharedMutations() 的方法后,action 生效;
綁定到組件上
<input type="text" name="regex-exp" @input='execRegex' value='regexExp' />
大體上就是這樣,剩余就是處理方法的邏輯,在 Mutation 的方法中實現;
Vue 生命周期問題:
為了讓 v-html 中的事件生效,我查閱了 vue 組件的生命周期;
v-html 在組件 mounted 之后,也就是 state 變化之后渲染,所以我們必須在 updated 之后綁定事件
生成桌面應用
npm run build:mas # 生成mac應用
npm run build:linux # 生成linux 應用
npm run build:win32 # 生成windows應用
可以在 /build 目錄中看到生成的應用目錄
對mac 應用進行打包:
將生成的應用放入新建的目錄,命名為 IT-Tools,再復制“應用程序”的軟鏈 到 IT-Tools目錄中;
cmd+空格:打開文件搜索 disk ,打開磁盤管理,點擊文件-新建映像-來自文件夾的映像;