1. 程式人生 > >【electron+vue3+ts實戰便箋exe】一、搭建框架配置

【electron+vue3+ts實戰便箋exe】一、搭建框架配置

> 不要讓自己的上限成為你的底線 ## 前言 詐屍更新系列,為了跟上腳步,嘗試了vue3,在學習vue3的時候順便學習一手electron和ts,本教程將分別釋出,原始碼會在最後的文章釋出。因為還在開發中,目前也是為了防止有些遇到坑不會及時忘掉,所以先整理一部分出來 > 將分2部分發出教程,因為配置的東西個人感覺有點多並且跟開發內容相關的東西並不是很多,因此單獨發出,見諒,第二篇這兩天發出,長文警告!⚠。 開發思路: 1. 頁面: - 列表頁`index.vue` 頭部、搜尋、內容部分,只能有一個列表頁存在 - 設定頁`setting.vue` 設定內容和軟體資訊,和列表頁一樣只能有一個存在 - 編輯頁 `editor.vue` icons功能和背景顏色功能,可以多個編輯頁同時存在 2. 動效: - 開啟動效,有一個放大、透明度的過渡,放不了動圖這裡暫時不演示了。 - 標題過渡效果 - 切換`index`和`setting`時頭部不變,內容過渡 3. 資料儲存:資料的建立和更新都在編輯頁`editor.vue`進行,這個過程中在儲存進`nedb`之後才通訊列表頁`index.vue`更新內容,考慮到效能問題,這裡使用了`防抖`防止連續性的更新而導致卡頓(不過貌似沒有這個必要。。也算是一個小功能吧,然後可以設定這個更新速度) 4. 錯誤採集:採集在使用中的錯誤並彈窗提示 5. 編輯顯示:`document`暴露 `execCommand` 方法,該方法允許執行命令來操縱可編輯內容區域的元素。 在開發的時候還遇到過好多坑,這些都是在`electron`環境中才有,比如 1. `@input`觸發2次,加上`v-model`觸發3次。包括建立一個新的electron框架也是這樣,別人電腦上不會出現這個問題,猜測是`electron快取`問題 2. vue3碰到`空屬性`報錯時無限報錯,在普通瀏覽器(edge和chrome)是正常一次 3. 元件無法正常渲染不報錯,只在控制檯報異常 4. 打包後由於`electron`的快取導致開啟需要10秒左右,清除c盤軟體快取後正常 其他的不記得了。。 這裡暫時不提供vue3和electron介紹,有需要的可以先看看社群其他的有關文章或者後期再詳細專門提供。軟體命名為`i-notes`。 vue3中文教程 https://vue3js.cn/docs/zh/guide/migration/introduction.html electron教程 https://www.electronjs.org/ typescript教程 https://www.typescriptlang.org/ > `electron-vue`裡面的包環境太低了,所以是手動配置electron+vue3(雖然說是手動。。其實就兩個步驟)
目錄結構 ``` electron-vue-notes ├── public │ ├── css │ ├── font │ └── index.html ├── src │ ├── assets │ │ └── empty-content.svg │ ├── components │ │ ├── message │ │ ├── rightClick │ │ ├── editor.vue │ │ ├── header.vue │ │ ├── input.vue │ │ ├── messageBox.vue │ │ ├── switch.vue │ │ └── tick.vue │ ├── config │ │ ├── browser.options.ts │ │ ├── classNames.options.ts │ │ ├── editorIcons.options.ts │ │ ├── index.ts │ │ └── shortcuts.keys.ts │ ├── inotedb │ │ └── index.ts │ ├── less │ │ └── index.less │ ├── router │ │ └── index.ts │ ├── script │ │ └── deleteBuild.js │ ├── store │ │ ├── exeConfig.state.ts │ │ └── index.ts │ ├── utils │ │ ├── errorLog.ts │ │ └── index.ts │ ├── views │ │ ├── editor.vue │ │ ├── index.vue │ │ ├── main.vue │ │ └── setting.vue │ ├── App.vue │ ├── background.ts │ ├── main.ts │ └── shims-vue.d.ts ├── .browserslistrc ├── .eslintrc.js ├── .prettierrc.js ├── babel.config.js ├── inoteError.log ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── tsconfig.json ├── vue.config.js └── yarn.lock ```
## 使用腳手架搭建vue3環境 沒有腳手架的可以先安裝腳手架 ``` npm install -g @vue/cli ``` 建立vue3專案 ``` vue create electron-vue-notes # 後續 ? Please pick a preset: (Use arrow keys) Default ([Vue 2] babel, eslint) Default (Vue 3 Preview) ([Vue 3] babel, eslint) > Manually select features # 手動選擇配置 # 後續所有配置 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Router, CSS Pre-processors, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use class-style component syntax? Yes ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Use history mode for router? (Requires proper server setup for index fallback in production) No ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Less ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save, Lint and fix on commit ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? (y/N) n ``` ![](https://img2020.cnblogs.com/blog/1099042/202012/1099042-20201225170338222-472340241.jpg) 建立完之後的目錄是這樣的 ``` electron-vue-notes ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── router │ │ └── index.ts │ ├── views │ │ ├── About.vue │ │ └── Home.vue │ ├── App.vue │ ├── main.ts │ └── shims-vue.d.ts ├── .browserslistrc ├── .eslintrc.js ├── babel.config.js ├── package.json ├── README.md ├── tsconfig.json └── yarn.lock ``` ## 安裝electron的依賴 ``` # yarn yarn add vue-cli-plugin-electron-builder electron # npm 或 cnpm npm i vue-cli-plugin-electron-builder electron ``` 安裝完之後完善一些配置,比如`別名`、`eslint`、`prettier`等等基礎配置,還有一些`顏色`、`icons`等等具體可以看下面 ## 專案的一些基礎配置 ### eslint 使用eslint主要是規範程式碼風格,不推薦tslint是因為tslint已經不更新了,tslint也推薦使用eslint 安裝eslint ``` npm i eslint -g ``` 進入專案之後初始化eslint ``` eslint --init # 後續配置 ? How would you like to use ESLint? To check syntax and find problems ? What type of modules does your project use? JavaScript modules (import/export) ? Which framework does your project use? Vue.js ? Does your project use TypeScript? Yes ? Where does your code run? Browser, Node ? What format do you want your config file to be in? JavaScript The config that you've selected requires the following dependencies: eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest ? Would you like to install them now with npm? (Y/n) y ``` ![](https://img2020.cnblogs.com/blog/1099042/202012/1099042-20201225170435468-53167726.jpg) 修改eslint配置,·`.eslintrc.js`,規則`rules`可以根據自己的喜歡配置 https://eslint.org/docs/user-guide/configuring ``` js module.exports = { root: true, env: { node: true }, extends: [ 'plugin:vue/vue3-essential', 'eslint:recommended', 'plugin:prettier/recommended', 'plugin:@typescript-eslint/eslint-recommended', '@vue/typescript/recommended', '@vue/prettier', '@vue/prettier/@typescript-eslint' ], parserOptions: { ecmaVersion: 2020 }, rules: { quotes: [1, 'single'], semi: 1, '@typescript-eslint/camelcase': 0, '@typescript-eslint/no-explicit-any': 0, 'no-irregular-whitespace': 2, 'no-case-declarations': 0, 'no-undef': 0, 'eol-last': 1, 'block-scoped-var': 2, 'comma-dangle': [2, 'never'], 'no-dupe-keys': 2, 'no-empty': 1, 'no-extra-semi': 2, 'no-multiple-empty-lines': [1, { max: 1, maxEOF: 1 }], 'no-trailing-spaces': 1, 'semi-spacing': [2, { before: false, after: true }], 'no-unreachable': 1, 'space-infix-ops': 1, 'spaced-comment': 1, 'no-var': 2, 'no-multi-spaces': 2, 'comma-spacing': 1 } }; ``` ### prettier 在根目錄增加`.prettierrc.js`配置,根據自己的喜好進行配置,單行多少個字元、單引號、分號、逗號結尾等等 ``` js module.exports = { printWidth: 120, singleQuote: true, semi: true, trailingComma: 'none' }; ``` ### tsconfig.json 如果這裡沒有配置識別`@/`路徑的話,在專案中使用會報錯 ``` json "paths": { "@/*": [ "src/*" ] } ``` ### package.json ``` json "author": "heiyehk", "description": "I便箋個人開發者heiyehk獨立開發,在Windows中更方便的記錄文字。", "main": "background.js", "scripts": { "lint": "vue-cli-service lint", "electron:build": "vue-cli-service electron:build", "electron:serve": "vue-cli-service electron:serve" } ``` ### 配置入口檔案`background.ts` 因為需要做一些開啟和關閉的動效,因此我們需要配置`electron`為`frame無邊框`和`透明transparent`的屬性 ``` ts /* eslint-disable @typescript-eslint/no-empty-function */ 'use strict'; import { app, protocol, BrowserWindow, globalShortcut } from 'electron'; import { createProtocol // installVueDevtools } from 'vue-cli-plugin-electron-builder/lib'; const isDevelopment = process.env.NODE_ENV !== 'production'; let win: BrowserWindow | null; protocol.registerSchemesAsPrivileged([ { scheme: 'app', privileges: { secure: true, standard: true } } ]); function createWindow() { win = new BrowserWindow({ frame: false, // 無邊框 hasShadow: false, transparent: true, // 透明 width: 950, height: 600, webPreferences: { enableRemoteModule: true, nodeIntegration: true } }); if (process.env.WEBPACK_DEV_SERVER_URL) { win.loadURL(process.env.WEBPACK_DEV_SERVER_URL); if (!process.env.IS_TEST) win.webContents.openDevTools(); } else { createProtocol('app'); win.loadURL('http://localhost:8080'); } win.on('closed', () => { win = null; }); } app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (win === null) { createWindow(); } }); app.on('ready', async () => { // 這裡註釋掉是因為會安裝tools外掛,需要遮蔽掉,有能力的話可以開啟註釋 // if (isDevelopment && !process.env.IS_TEST) { // try { // await installVueDevtools(); // } catch (e) { // console.error('Vue Devtools failed to install:', e.toString()); // } // } createWindow(); }); if (isDevelopment) { if (process.platform === 'win32') { process.on('message', data => { if (data === 'graceful-exit') { app.quit(); } }); } else { process.on('SIGTERM', () => { app.quit(); }); } } ``` ### 啟動 ``` sh yarn electron:serve ``` ![](https://img2020.cnblogs.com/blog/1099042/202012/1099042-20201225170449656-2115865981.jpg) 到這裡配置就算是成功搭建好這個視窗了,但是還有一些其他細節需要進行配置,比如`electron打包`配置,`模組化`的配置等等 ## 常規配置 這裡配置一些常用的開發內容和一些輪子程式碼 ### reset.csss ``` css html{font-family:'Microsoft YaHei UI','Microsoft YaHei',sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}address,applet,article,aside,audio,blockquote,body,canvas,caption,dd,details,div,dl,dt,embed,figcaption,figure,footer,h1,h2,h3,h4,h5,h6,header,html,iframe,li,mark,menu,nav,object,ol,output,p,pre,progress,ruby,section,summary,table,tbody,td,tfoot,th,thead,time,tr,ul,video{margin:0;padding:0;border:0;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent;text-decoration:none}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0;line-height:normal}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}::-ms-clear,::-ms-reveal{display:none}input:-webkit-autofill{-webkit-animation:autofill-fix 1s infinite!important;-webkit-text-fill-color:#666;-webkit-transition:background-color 50000s ease-in-out 0s!important;transition:background-color 50000s ease-in-out 0s!important;background-color:transparent!important;background-image:none!important;-webkit-box-shadow:0 0 0 1000px transparent inset!important}[role=button],a,area,button,input:not([type=range]),label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}input[type=number],input[type=password],input[type=text],textarea{-webkit-appearance:none} ``` ### common.css ``` css /* 空元素顯示的內容 */ .empty-content:empty::before { /* content: attr(placeholder); */ content: '記筆記...'; font-size: 14px; color: #666; line-height: 21px; } /* 隱藏滾動條 */ ::-webkit-scrollbar { width: 0; height: 0; } /* 設定ol顯示格式 */ .module-editor ol { counter-reset:sectioncounter; } .module-editor ol li { list-style: none; position: relative; } .module-editor ol li::before { content: counter(sectioncounter) '.'; counter-increment:sectioncounter; margin-right: 10px; } /* 使用自定義偽類會導致游標偏移向下 */ /* .module-editor ul { position: relative; } .module-editor ul li { list-style-type: none; word-break: break-all; } .module-editor ul li::before { content: ''; width: 5px; height: 5px; background-color: #000; margin-right: 6px; display: inline-block; border-radius: 100%; transform: translateY(-2px); margin-left: 1px; } */ .module-editor ul li { word-break: break-all; list-style: disc inside; } /* 常用flex佈局 */ .flex { display: flex; } .flex-center { display: flex; justify-content: center; align-items: center; } .flex-left { display: flex; justify-content: center; align-items: flex-start; } .flex-right { display: flex; justify-content: center; align-items: flex-end; } .flex-items { display: flex; align-items: center; } .flex-between { display: flex; justify-content: space-between; align-items: center; } .flex1 { flex: 1; } /* ellips */ .hidden { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } html, body, .app, .transition, .bg-white { width: 100%; height: 100%; box-sizing: border-box; position: relative; overflow: hidden; background-color: rgba(0, 0, 0, 0); outline: none; } .bg-white { background-color: #fff; } /* 軟體陰影 */ .app { box-shadow: 0 0 4px rgb(185, 185, 185); } body { padding: 4px; user-select: none; outline: none; } @keyframes fadein { 0% { transform: scale(0.8); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } @keyframes fadeout { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(0.9); opacity: 0; } } /* 進入和退出動效 */ .app-show { animation: fadein 0.4s forwards; transform: scale(1) !important; } .app-hide { animation: fadeout 0.2s forwards; transform: scale(0.9); } /* 顏色 */ .yellow-content { transition: background-color 0.4s; background-color: #fff7d1 !important; } .green-content { transition: background-color 0.4s; background-color: #e4f9e0 !important; } .pink-content { transition: background-color 0.4s; background-color: #ffe4f1 !important; } .purple-content { transition: background-color 0.4s; background-color: #f2e6ff !important; } .blue-content { transition: background-color 0.4s; background-color: #e2f1ff !important; } .gray-content { transition: background-color 0.4s; background-color: #f3f2f1 !important; } .black-content { transition: background-color 0.4s; background-color: #696969 !important; color: #fff; } .black-content * { color: #fff; } ``` ### config 這個對應專案中的config資料夾 ``` config ├── browser.options.ts # 視窗的配置 ├── classNames.options.ts # 樣式名的配置,背景樣式都通過這個檔案渲染 ├── editorIcons.options.ts # 編輯頁面的一些editor圖示 ├── index.ts # 匯出 └── shortcuts.keys.ts # 禁用的一些快捷鍵,electron是基於chromium瀏覽器,所以也存在一些瀏覽器快捷鍵比如F5 ``` #### browser.options 這個檔案的主要作用就是配置主視窗和編輯視窗區分開發正式的配置,寬高等等,以及要顯示的主頁面 ``` ts /** * 軟體資料和配置 * C:\Users\{使用者名稱}\AppData\Roaming * 共享 * C:\ProgramData\Intel\ShaderCache\i-notes{xx} * 快捷方式 * C:\Users\{使用者名稱}\AppData\Roaming\Microsoft\Windows\Recent * 電腦自動建立快取 * C:\Windows\Prefetch\I-NOTES.EXE{xx} */ /** */ const globalEnv = process.env.NODE_ENV; const devWid = globalEnv === 'development' ? 950 : 0; const devHei = globalEnv === 'development' ? 600 : 0; // 底部icon: 40*40 const editorWindowOptions = { width: devWid || 290, height: devHei || 350, minWidth: 250 }; /** * BrowserWindow的配置項 * @param type 單獨給編輯視窗的配置 */ const browserWindowOption = (type?: 'editor'): Electron.BrowserWindowConstructorOptions => { const commonOptions = { minHeight: 48, frame: false, hasShadow: true, transparent: true, webPreferences: { enableRemoteModule: true, nodeIntegration: true } }; if (!type) { return { width: devWid || 350, height: devHei || 600, minWidth: 320, ...commonOptions }; } return { ...editorWindowOptions, ...commonOptions }; }; /** * 開發環境: http://localhost:8080 * 正式環境: file://${__dirname}/index.html */ const winURL = globalEnv === 'development' ? 'http://localhost:8080' : `file://${__dirname}/index.html`; export { browserWindowOption, winURL }; ``` #### classNames.options 如果想要更多的顏色直接新增即可 ``` ts /** * - `yellow-content` 黃色 * - `green-content` 綠色 * - `pink-content` 粉色 * - `purple-content` 紫色 * - `blue-content` 藍色 * - `gray-content` 灰色 * - `black-content` 黑色 */ const classNames = [ // { // color: 'white-content', // title: '白色' // }, { className: 'yellow-content', title: '黃色' }, { className: 'green-content', title: '綠色' }, { className: 'pink-content', title: '粉色' }, { className: 'purple-content', title: '紫色' }, { className: 'blue-content', title: '藍色' }, { className: 'gray-content', title: '灰色' }, { className: 'black-content', title: '黑色' } ]; export default classNames; ```` #### editorIcons.options ``` ts /** * - `bold` 加粗 * - `italic` 斜體 * - `underline` 下劃線 * - `strikethrough` 刪除線 * - `insertUnorderedList` 無序列表 * - `insertOrderedList` 有序列表 * - `image` 圖片 */ const editorIcons = [ { name: 'bold', title: '加粗', icon: 'icon-editor-bold' }, { name: 'italic', title: '斜體', icon: 'icon-italic' }, { name: 'underline', title: '下劃線', icon: 'icon-underline' }, { name: 'strikethrough', title: '刪除線', icon: 'icon-strikethrough' }, { name: 'insertUnorderedList', title: '無序列表', icon: 'icon-ul' }, { name: 'insertOrderedList', title: '有序列表', icon: 'icon-ol' // }, // { // name: 'image', // title: '圖片', // icon: 'icon-image' } ]; export default editorIcons; ``` #### shortcuts.keys ``` ts /** * - F11 禁用全屏放大 * - CTRL+R 禁用重新整理 * - CTRL+SHIFT+R 禁用重新整理 */ const devShortcuts = ['F11', 'Ctrl+R', 'Ctrl+SHIFT+R']; // 這裡主要是在開發中需要用到,但是正式環境需要遮蔽的快捷鍵 const shortcuts = ['Ctrl+N', 'SHIFT+F10', 'Ctrl+SHIFT+I']; // 這裡是直接遮蔽掉的快捷鍵 const exportKeys = process.env.NODE_ENV === 'development' ? shortcuts : [...devShortcuts, ...shortcuts]; export default exportKeys; ``` #### index 匯出 ``` ts import classNames from './classNames.options'; import editorIcons from './editorIcons.options'; import { browserWindowOption, winURL } from './browser.options'; import shortcutsKeys from './shortcuts.keys'; export { classNames, editorIcons, browserWindowOption, winURL, shortcutsKeys }; ``` #### 調整background.ts 配置完config之後需要再調整一下`background.ts` ``` ts // 修改createWindow方法 function createWindow() { win = new BrowserWindow(browserWindowOption()); if (process.env.WEBPACK_DEV_SERVER_URL) { win.loadURL(process.env.WEBPACK_DEV_SERVER_URL); if (!process.env.IS_TEST) win.webContents.openDevTools(); } else { createProtocol('app'); win.loadURL(winURL); } win.on('closed', () => { win = null; }); } ... app.on('ready', async () => { ... // 快捷鍵禁用 for (const key of shortcutsKeys) { globalShortcut.register(key, () => {}); } createWindow(); }); ``` ### vue.config.js #### 建立vue.config.js檔案 ``` js /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); module.exports = { productionSourceMap: false, configureWebpack: config => { if (process.env.NODE_ENV !== 'development') {/ // 清除開發中debug和console.log等等 config.optimization.minimizer[0].options.terserOptions.warnings = false; config.optimization.minimizer[0].options.terserOptions.compress = { warnings: false, drop_console: true, drop_debugger: true, pure_funcs: ['console.log'] }; } }, pluginOptions: { // 這裡是electronbuild的配置資訊 electronBuilder: { // 這裡是在瀏覽器中使用node環境,需要為true nodeIntegration: true, builderOptions: { productName: 'I便箋', appId: 'com.inote.heiyehk', copyright: 'heiyehk', compression: 'store', // "store" | "normal"| "maximum" 打包壓縮情況(store 相對較快),store 39749kb, maximum 39186kb // directories: { // output: 'build' // 輸出資料夾 // }, win: { // icon: 'xxx/icon.ico', target: ['nsis', 'zip'] }, mac: { target: { target: 'dir', arch: 'arm64' } }, nsis: { oneClick: false, // 一鍵安裝 // guid: 'xxxx', // 登錄檔名字,不推薦修改 perMachine: true, // 是否開啟安裝時許可權限制(此電腦或當前使用者) allowElevation: true, // 允許請求提升。 如果為false,則使用者必須使用提升的許可權重新啟動安裝程式。 allowToChangeInstallationDirectory: true, // 允許修改安裝目錄 // installerIcon: './build/icons/aaa.ico', // 安裝圖示 // uninstallerIcon: './build/icons/bbb.ico', // 解除安裝圖示 // installerHeaderIcon: './build/icons/aaa.ico', // 安裝時頭部圖示 createDesktopShortcut: true, // 建立桌面圖示 createStartMenuShortcut: true, // 建立開始選單圖示 shortcutName: 'i便箋' // 圖示名稱 } } }, 'style-resources-loader': { preProcessor: 'less', patterns: [path.resolve(__dirname, 'src/less/index.less')] // 引入全域性樣式變數 } } }; ``` #### 使用全域性less變數 這裡需要使用全域性less變數,安裝`style-resources-loader`,安裝完之後會預設在vue.config.js中配置,只需要修改一下路徑即可 ``` vue add style-resources-loader ``` #### index.less配置 ``` less @primary-color: #027aff; @success-color: #19be6b; @warning-color: #ff9900; @error-color: #ed4014; @white-color: #ffffff; @gray-color: #efefef; @text-color: #000000; @text-sub-color: #00000073; @border-color: #d9d9d9; @disabled-color: #c5c8ce; @background-color: #f3f3f3; @background-sub-color: #eeeeee; @shadown-color: #cccccc; // 頭部iconsize @headerIconFontSize: 22px; // 頭部高度、底部功能按鈕和icon的寬高大小是一致的 @iconSize: 40px; .icon { width: @iconSize; height: @iconSize; min-width: @iconSize; min-height: @iconSize; outline: none; border: none; background-color: transparent; padding: 0; position: relative; &::before { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 0; } a { color: initial; width: 100%; height: 100%; outline: none; position: relative; z-index: 1; } .iconfont { width: 22px; position: relative; } &:hover { &::before { background-color: rgba(0, 0, 0, 0.1); } } } ```