整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          vite+vue3實現網頁版編輯器,帶高亮以及代碼提

          《vite+vue3實現網頁版編輯器,帶高亮以及代碼提示(以SQL語言為例)》

          ## 引言:探索Vite與Vue3結合構建高效Web應用

          隨著前端技術的飛速發展,Vite和Vue3已成為現代Web開發領域的熱門工具。Vite以其快速冷啟動、熱更新等特性讓開發者享受前所未有的開發體驗;而Vue3則憑借其優秀的組件化設計與Composition API,極大地提高了開發效率和代碼可維護性。本文將引導您如何利用這兩者搭建一款功能齊全、性能卓越的網頁版SQL編輯器,包括代碼高亮顯示及智能提示等功能。

          ## 一、項目初始化與環境配置

          ### 1. 創建項目

          首先,確保已安裝Node.js和npm。然后通過Vite創建一個基于Vue3的新項目:

          ```bash

          npm create vite@latest my-sql-editor --template vue

          cd my-sql-editor

          npm install

          ```

          ### 2. 安裝相關依賴

          為了實現實時語法高亮和代碼提示,我們需要借助`codemirror`庫及其SQL相關的插件:

          ```bash

          npm install codemirror @codemirror/lang-sql

          ```

          ## 二、編寫基礎HTML結構與Vue組件

          ### 1. 在App.vue中引入CodeMirror

          ```html

          <template>

          <div id="app">

          <textarea ref="editor"></textarea>

          </div>

          </template>

          <script setup lang="ts">

          import { onMounted, ref } from 'vue';

          import CodeMirror from 'codemirror';

          onMounted(() => {

          const editor = CodeMirror.fromTextArea(

          document.querySelector('textarea'),

          {

          mode: 'text/x-sql',

          lineNumbers: true,

          theme: 'dracula', // 更多主題可以自定義選擇

          }

          );

          });

          </script>

          ```

          此處我們已在App.vue中引入并初始化了一個基本的CodeMirror編輯器,并設置SQL模式以支持初步的語義高亮。

          ## 三、實現SQL代碼高亮

          上述代碼已經實現了基礎的SQL高亮,CodeMirror內置了對SQL的支持。但為了讓效果更佳,我們可以進一步優化配置項,如添加SQL關鍵字高亮等。

          ### 高級配置示例:

          ```javascript

          import '@codemirror/theme-dracula'; // 引入主題樣式

          import { Extension } from '@codemirror/state';

          const sqlExtensions: Extension[] = [

          // SQL語言插件

          langSql(),

          // 添加代碼行號

          lineNumbers(),

          // 設置主題

          EditorView.theme({

          '&': { background: '#282a36' },

          '.cm-comment': { color: '#6272a4' }, // 注釋顏色

          '.cm-keyword': { color: '#ff79c6' }, // 關鍵字顏色

          // ...其他樣式自定義

          }),

          ];

          onMounted(() => {

          const editor = CodeMirror.fromTextArea(

          document.querySelector('textarea'),

          {

          extensions: sqlExtensions,

          }

          );

          });

          ```

          ## 四、實現SQL代碼提示

          CodeMirror并沒有直接提供SQL的自動補全功能,但我們可以通過自定義擴展來實現。這里我們使用`hint`和`autocomplete`插件配合自定義數據源實現SQL代碼提示。

          ### 實現代碼提示功能:

          ```javascript

          // 假設我們有一個包含所有SQL關鍵字和函數的數組

          const sqlKeywords = ['SELECT', 'FROM', 'WHERE', 'LIKE', /*...*/ ];

          function sqlHint(cm: EditorView) {

          let cur = cm.state.field(EditorState.cursor).head;

          let token = cm.getTokenAt(cur);


          if (token.string.startsWith('@')) { // 示例:針對特定字符開頭觸發提示

          let list: string[] = [];

          for (let keyword of sqlKeywords) {

          if (keyword.startsWith(token.string.slice(1))) {

          list.push(keyword);

          }

          }

          return {

          from: cm.posFromIndex(cur - token.start),

          to: cm.posFromIndex(cur),

          list: list,

          };

          }

          }

          const hintExtension = [

          Completion.of([

          { provide: ['completion'], get: () => sqlHint },

          ]),

          ];

          sqlExtensions.push(...hintExtension);

          ```

          ## 結語:進階優化與未來展望

          至此,我們已成功利用Vite+Vue3構建了一個具備SQL高亮和代碼提示功能的網頁版編輯器。然而,為了提升用戶體驗,還可以在此基礎上進行諸如錯誤檢測、實時預覽查詢結果等更多高級功能的開發。同時,對于SQL提示的完善,可以考慮接入數據庫API獲取實時表結構信息,實現更精準的智能提示。希望本文能為您的前端開發之旅注入新的靈感與動力,讓我們一起在前端世界里創造更多可能!

          ue (讀音 /vju?/,類似于 view) 是一套用于構建用戶界面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,當與現代化的工具鏈以及各種支持類庫結合使用時,Vue 也完全能夠為復雜的單頁應用提供驅動。

          Vue前端入門,第9節 v-text的簡單學習

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="js/vue.js"></script>
              <script src="js/axios.js"></script>
          </head>
          <body>
          <div id="app">
              {{name}}
              <!--    屬性設置-->
              <h3 v-text="name">這里不顯示</h3>
              <h3>武當掌門:{{name}}</h3>
              <!--    字符串的拼接-->
              <h3 v-text="`武當掌門:`+name"></h3>
              <h3 v-text="name + '!!!'"></h3>
              <h3>xxx{{name + "!!!"}}</h3>
          
          </div>
          <script>
              new Vue({
                  el: "#app",
                  data: {
                      "name": "張三豐"
                  }
              })
          </script>
          </body>
          </html>

          {{ var }} 與 v-text:"var"

          <!--    更新元素的 textContent (h3)。h3內的文本(這里不顯示)將不顯示-->
          <h3 v-text="name">這里不顯示</h3>
          <!--    如果需要更新部分的textContent,那么可以使用插值語法{{name}}。-->
          <h3>武當掌門:{{name}}</h3>

          字符串拼接

          v-text的拼接

          v-text="`武當掌門:`+name"
          v-text="name + '!!!'"

          這兩種方法都是允許的

          家好,我是Echa 哥,祝大家開工大吉!萬事如意!

          前言

          vue作為前端主流的3大框架之一,目前在國內有著非常廣泛的應用,由于其輕量和自底向上的漸進式設計思想,使其不僅僅被應用于PC系統,對于移動端,桌面軟件(electronjs)等也有廣泛的應用,與此誕生的優秀的開源框架比如elementUI,iView, ant-design-vue等也極大的降低了開發者的開發成本,并極大的提高了開發效率。筆者最初接觸vue時也是使用的iview框架,親自體會之后確實非常易用且強大。

          筆者曾經有兩年的vue項目經驗,基于vue做過移動端項目和PC端的ERP系統,雖然平時工作中采用的是react技術棧,但平時還是會積累很多vue相關的最佳實踐和做一些基于vue的開源項目,所以說總結vue的項目經驗我覺得是最好的成長,也希望給今年想接觸vue框架或者想從事vue工作的朋友帶來一些經驗和思考。

          你將收獲

          • vue框架使用注意事項和最佳經驗
          • vue項目配置經驗總結
          • vue組件設計經驗總結
          • vue項目架構與服務化探索

          正文

          本文不僅僅是總結一些vue使用踩過的一些坑和項目經驗,更多的是使用框架(vue/react)過程中的方法論和組件的設計思路,最后還會有一些個人對工程化的一些總結,希望有更多經驗的朋友們可以一起交流,探索vue的奧妙。

          在開始文章之前,筆者建議大家對javascript, css, html基礎有一定的了解,因為會用框架不一定能很好的實現業務需求和功能,要想實現不同場景下不同復雜度的需求,一定要對web基礎有充足的了解,所以希望大家熟悉如下基礎知識,如果不太熟悉可以花時間研究了解一下。

          javascript:

          • 數組常用方法的使用,比如遍歷有forEach,map,filter,every, some,reduce,操作方法有splice,slice, join,push,shift, pop,sort等
          • 基本數據結構,引用類型(對象,數組)
          • 基本邏輯運算if else, switch,三目運算:?,for/while循環等
          • 字符串常見api(如replace,slice, substr,indexOf)
          • 基本正則使用
          • 變量作用域,作用域鏈,變量提升,函數聲明提升
          • 對象基本用法,面向對象編程

          css:

          • 基本盒模型(border/content/padding等)
          • 4種常用定位(static/absolute/relative/fixed)
          • 常用布局方式(浮動布局/彈性布局flex/自適應布局/網格布局grid)
          • css3基本樣式與動畫(transition,animation)

          html:

          • 新標簽基本用法和使用
          • head標簽作用與用法(主要是meta屬性的用法)

          所以希望大家掌握好以上基礎知識,也是前端開發的基礎,接下來我們直接進入正文。

          1. vue框架使用注意事項和最佳經驗

          ?

          vue學習最快的方式就是實踐,根據官網多寫幾個例子是掌握vue最快的方式。 接下來筆者就來總結一下在開發vue項目中的一些實踐經驗。

          ?

          1.1 vue生命周期以及不同生命周期下的應用

          以上是vue官網上的生命周期的方法,大致劃分一下分為創建前/后,掛載前/后,更新前/后,銷毀前/后這四個階段。各個階段的狀態總結如下:

          • beforeCreate:在beforeCreate生命周期執行時,data和methods中的數據還未初始化,所以此時不能使用data中的數據和methods中的方法
          • create:data 和 methods初始化完畢,此時可以使用methods 中的方法和data 中的數據
          • beforeMount:template模版已經編譯好,但還未掛載到頁面,此時頁面還是上一個狀態
          • mounted:此時Vue實例初始化完成了,DOM掛載完畢,可以直接操作dom或者使用第三方dom庫
          • beforeUpdate: 此時data已更新,但還未同步頁面
          • updated:data和頁面都已經更新完成
          • beforeDestory:Vue實例進入銷毀階段,但所有的 data 和 methods , 指令, 過濾器等都處于可用狀態
          • destroyed: 此時組件已經被銷毀,data,methods等都不可用

          根據以上介紹,頁面第一次加載時會執行 beforeCreate, created, beforeMount, mounted這四個生命周期,所以我們一般在created階段處理http請求獲取數據或者對data做一定的處理, 我們會在mounted階段操作dom,比如使用jquery,或者其他第三方dom庫。其次,根據以上不同周期下數據和頁面狀態的不同,我們還可以做其他更多操作,所以說每個生命周期的發展狀態非常重要,一定要理解,這樣才能對vue有更多的控制權。

          1.2 vue常用的指令以及動態指令的使用

          指令 (Directives) 是帶有 v- 前綴的特殊屬性,vue常用的指令有:

          • v-bind 用于響應式地更新 HTML屬性
          • v-if 根據表達式的值的真假來決定是否插入/移除元素
          • v-on 用于監聽 DOM 事件
          • v-show 用于決定是否展示該元素,底層通過display:none實現
          • v-html 在dom內插入html內容
          • v-for 循環
          • v-text 渲染指定dom的內容文本
          • v-cloak 和CSS規則如 [v-cloak] { display: none } 一起用,可以隱藏未編譯的 Mustache 標簽直到實例準備完畢

          以上是比較常用的指令,具體用法就不一一舉例了,其中v-cloak主要是用來避免頁面加載時出現閃爍的問題,可以結合css的[v-cloak] { display: none }方式解決這一問題。關于指令的動態參數,使用也很簡單,雖然是2.6.0 新增的,但是方法很靈活,具體使用如下:

          <a v-on:[eventName]="doSomething"> ... </a>  
          復制代碼
          

          我們可以根據具體情況動態切換事件名,從而綁定同一個函數。

          1.3 vue常用修飾符及作用

          1. 事件修飾符
          • .stop 阻止事件冒泡
          • .prevent 阻止事件默認行為
          • .self 事件綁定的元素本身觸發時才觸發回調
          • .once 事件只能觸發一次,第二次就不會觸發了
          • .native 將一個vue組件變成一個普通的html,使其可以監聽click等原生事件 具體使用如下:
          <Tag @click.native="handleClick">ok</Tag>  
          復制代碼
          
          1. 表單修飾符
          • .lazy 在輸入框輸入完內容,光標離開時才更新視圖
          • .trim 過濾首尾空格
          • .number 如果先輸入數字,那它就會限制你輸入的只能是數字;如果先輸入字符串,那就相當于沒有加.number

          用法如下:

          <input type="text" v-model.trim="value">  
          復制代碼
          

          還有很多修飾符比如鍵盤,鼠標等修飾符,感興趣的大家可以自行學習研究。

          1.4 組件之間,父子組件之間的通信方案

          組件之間的通信方案:

          • 通過事件總線(bus),即通過發布訂閱的方式
          • vuex

          父子組件:

          • 父組件通過prop向自組件傳遞數據
          • 子組件綁定自定義事件,通過this.$emit(event,params) 來調用自定義事件
          • 使用vue提供的 children & $refs方法來通信

          1.5 vue實現按需加載組件

          組件的按需加載是項目性能優化的一個環節,也可以降低首屏渲染時間,筆者在項目中用到的組件按需加載的方式如下:

          1. 使用() => import(), 具體代碼如下:
          <template>  
              <div>  
                 <ComponentA />  
                 <ComponentB />  
              </div>  
          </template>  
          <script>  
          const ComponentA = () => import('./ComponentA')  
          const ComponentB = () => import('./ComponentB')  
          export default {  
              // ...  
              components: {  
              ComponentA,  
              ComponentB  
              },  
              // ...  
          }  
          </script>  
          復制代碼
          
          1. 使用resolve => require(['./ComponentA'], resolve),使用方法如下:
          <template>  
              <div>  
                 <ComponentA />  
              </div>  
          </template>  
          <script>  
          const ComponentA = resolve => require(['./ComponentA'], resolve)  
          export default {  
              // ...  
              components: {  
              ComponentA  
              },  
              // ...  
          }  
          </script>  
          復制代碼
          

          「1.6 vuex的幾種屬性和作用,以及使用vuex的基本模式」

          ?

          Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。

          ?

          vuex的基本工作模式如下圖所示:

          state的改變完全由mutations控制, 我們也沒必要任何項目都使用vuex,對于中大型復雜項目而言,需要共享的狀態很多時,使用vuex才是最佳的選擇。接下來我將詳細介紹各api的概念和作用。

          • state 單一狀態樹,用一個對象就包含了全部的應用層級狀態,并且作為一個唯一數據源而存在
          • getters 就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算 比如如下案例:
          const store = new Vuex.Store({  
            state: {  
              todos: [  
                { id: 1, text: '...', done: true },  
                { id: 2, text: '...', done: false }  
              ]  
            },  
            getters: {  
              doneTodos: state => {  
                return state.todos.filter(todo => todo.done)  
              }  
            }  
          })  
          // 訪問getters里的屬性  
          this.$store.getters.doneTodos  
          復制代碼
          
          • Mutation 更改 Vuex 的 store 中的狀態的唯一方法,使用案例如下:
          const store = new Vuex.Store({  
            state: {  
              num: 1  
            },  
            mutations: {  
              add (state) {  
                // 變更狀態  
                state.num++  
              }  
            }  
          })  
            
          // 在項目中使用mutation  
          store.commit('add')  
          // 添加額外參數  
          store.commit('add', 10)  
          復制代碼
          
          • Action Action提交的是mutation,而不是直接變更狀態,可以包含任意異步操作,具體用法如下:
          const store = new Vuex.Store({  
            state: {  
              num: 0  
            },  
            mutations: {  
              add (state) {  
                state.num++  
              }  
            },  
            actions: {  
              add (context) {  
                context.commit('add')  
              },  
              asyncAdd ({ commit }) {  
              setTimeout(() => {  
                commit('add')  
              }  
            }  
          })  
          // 分發action  
          store.dispatch('add')  
          // 異步action  
          store.dispatch('asyncAdd')  
          // 異步傳參  
          store.dispatch('asyncAdd', { num: 10 })  
          
          • Module 將store分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊

          筆者更具實際經驗總結了一套標準使用模式,就拿筆者之前的開源XPXMS舉例,如下:

          store目錄是用來組織vuex代碼用的,我將action,mutation,state分文件管理,這樣項目大了之后也很容易管理和查詢。接下來看看是如何組織的:

          // type.ts  
          // 用來定義state等的類型文件  
          export interface State {  
              name: string;  
              isLogin: boolean;  
              config: Config;  
              [propName: string]: any;  // 用來定義可選的額外屬性  
          }  
            
          export interface Config {  
              header: HeaderType,  
              banner: Banner,  
              bannerSider: BannerSider,  
              supportPay: SupportPay  
          }  
            
          export interface Response {  
              [propName: string]: any;  
          }  
            
          // state.ts  
          // 定義全局狀態  
          import { State } from './type'  
          export const state: State = {  
              name: '',  
              isLogin: false,  
              curScreen: '0', // 0為pc, 1為移動  
              config: {  
                  header: {  
                      columns: ['首頁', '產品', '技術', '運營', '商業'],  
                      height: '50',  
                      backgroundColor: '#000000',  
                      logo: ''  
                  }  
              },  
              // ...  
              articleDetail: null  
          };  
            
          // mutation.ts  
          import {  
              State,  
              Config,  
              HeaderType,  
              Banner,  
              BannerSider,  
              SupportPay  
          } from './type'  
            
          export default {  
              // 預覽模式  
              setScreen(state: State, payload: string) {  
                  state.curScreen = payload;  
              },  
            
              // 刪除banner圖  
              delBanner(state: State, payload: number) {  
                  state.config.banner.bannerList.splice(payload, 1);  
              },  
            
              // 添加banner圖  
              addBanner(state: State, payload: object) {  
                  state.config.banner.bannerList.push(payload);  
              },  
            
              // ...  
          };  
            
          // action.ts  
          import {  
              HeaderType,  
              Response  
           } from './type'  
          import http from '../utils/http'  
          import { uuid, formatTime } from '../utils/common'  
          import { message } from 'ant-design-vue'  
            
          export default {  
              /**配置 */  
              setConfig(context: any, paylod: HeaderType) {  
                  http.get('/config/all').then((res:Response) => {  
                      context.commit('setConfig', res.data)  
                  }).catch((err:any) => {  
                      message.error(err.data)  
                  })  
              },  
              /**header */  
              saveHeader(context: any, paylod: HeaderType) {  
                  http.post('/config/setHeader', paylod).then((res:Response) => {  
                      message.success(res.data)  
                      context.commit('saveHeader', paylod)  
                  }).catch((err:any) => {  
                      message.error(err.data)  
                  })  
              },  
              // ...  
          };  
            
          // index.ts  
          import Vue from 'vue';  
          import Vuex from 'vuex';  
          import { state } from './state';  
          import mutations from './mutation';  
          import actions from './action';  
            
          Vue.use(Vuex);  
            
          export default new Vuex.Store({  
            state,  
            mutations,  
            actions  
          });  
            
          // main.ts  
          // 最后掛載到入口文件的vue實例上  
          import Vue from 'vue';  
          import App from './App.vue';  
          import router from './router';  
          import store from './store/';  
          import './component-class-hooks';  
          import './registerServiceWorker';  
            
          Vue.config.productionTip = false;  
            
          new Vue({  
            router,  
            store,  
            render: (h) => h(App),  
          }).$mount('#app');  
          

          我們在實際項目中都可以使用這種方式組織管理vuex相關的代碼。

          「1.7 vue-router基本使用模式和導航鉤子的用法及作用」

          vue-router使用大家想必不是很陌生,這里直接寫一個案例:

          // router.ts  
          import Vue from 'vue';  
          import Router from 'vue-router';  
          import Home from './views/admin/Home.vue';  
            
          Vue.use(Router);  
            
          const router =  new Router({  
            mode: 'history',  
            base: process.env.BASE_URL,  
            routes: [  
              {  
                path: '/',  
                component: Home,  
                beforeEnter: (to, from, next) => {  
                  next();  
                },  
                children: [  
                  {  
                    // 當 /user/:id/profile 匹配成功,  
                    // UserProfile 會被渲染在 User 的 <router-view> 中  
                    path: '',  
                    name: 'header',  
                    component: () => import(/* webpackChunkName: "header" */ './views/admin/subpage/Header.vue'),  
                  },  
            
                  {  
                    path: '/banner',  
                    name: 'banner',  
                    component: () => import(/* webpackChunkName: "banner" */ './views/admin/subpage/Banner.vue'),  
                  },  
                  {  
                    path: '/admin',  
                    name: 'admin',  
                    component: () => import(/* webpackChunkName: "admin" */ './views/admin/Admin.vue'),  
                  },  
                ],  
              },  
              {  
                path: '/login',  
                name: 'login',  
                component: () => import(/* webpackChunkName: "login" */ './views/Login.vue'),  
                meta:{  
                  keepAlive:false //不需要被緩存的組件  
                }  
              },  
              {  
                path: '*',  
                name: '404',  
                component: () => import(/* webpackChunkName: "404" */ './views/404.vue'),  
              },  
            ],  
          });  
            
          // 路由導航鉤子的用法  
          router.beforeEach((to, from, next) => {  
            if(from.path.indexOf('/preview') < 0) {  
              sessionStorage.setItem('prevToPreviewPath', from.path);  
            }  
            next();  
          })  
            
          export default router  
          

          以上案例是很典型的靜態路由配置和導航鉤子的用法(如何加載路由組件,動態加載路由組件,404頁面路由配置,路由導航鉤子使用)。如果在做后臺系統,往往會涉及到權限系統,所以一般會采用動態配置路由,通過前后端約定的路由方式,路由配置文件更具不同用戶的權限由后端處理后返。由于設計細節比較繁瑣,涉及到前后端協定,所以這里只講思路就好了。

          1.8 vue中檢測變化的注意事項

          受現代 JavaScript 的限制,Vue 無法檢測到對象屬性的添加或刪除。由于 Vue 會在初始化實例時對屬性執行 getter/setter 轉化,所以屬性必須在 data 對象上存在才能讓 Vue 將它轉換為響應式的。還有一種情況是,vue無法檢測到data屬性值為數組或對象的修改,所以我們需要用原對象與要混合進去的對象的屬性一起創建一個新的對象。可以使用this.$set或者對象的深拷貝,如果是數組則可以使用splice,擴展運算符等方法來更新。

          1.9 對指定頁面使用keep-alive路由緩存

          keep-alive是Vue的內置組件,能在組件切換過程中將狀態保留在內存中,防止重復渲染DOM。我們可以使用以下方式設置某些頁面是否被緩存:

          1. 通過路由配置文件和router-view設置:
          // routes 配置  
          export default [  
            {  
              path: '/A',  
              name: 'A',  
              component: A,  
              meta: {  
                keepAlive: true // 需要被緩存  
              }  
            }, {  
              path: '/B',  
              name: 'B',  
              component: B,  
              meta: {  
                keepAlive: false // 不需要被緩存  
              }  
            }  
          ]  
          

          路由視圖配置:

          // 路由設置  
          <keep-alive>  
              <router-view v-if="$route.meta.keepAlive">  
                  <!-- 會被緩存的視圖組件-->  
              </router-view>  
          </keep-alive>  
            
          <router-view v-if="!$route.meta.keepAlive">  
              <!-- 不需要緩存的視圖組件-->  
          </router-view>  
          
          
            
          
          
          
          
          1. 通過router-view的key屬性 具體方式如下:
          <template>  
            <div id="app">  
              <keep-alive>  
                <router-view :key="key" />  
              </keep-alive>  
            </div>  
          </template>  
            
          <script lang="ts">  
          import { Vue } from 'vue-property-decorator';  
          import Component from 'vue-class-component';  
          @Component  
          export default class App extends Vue {  
            get key() {  
              // 緩存除預覽和登陸頁面之外的其他頁面  
              console.log(this.$route.path)  
              if(this.$route.path.indexOf('/preview') > -1) {  
                return '0'  
              }else if(this.$route.path === '/login') {  
                return '1'  
              }else {  
                return '2'  
              }  
            }  
          }  
          </script>  
          

          1.10 vue常用工具函數總結

          總結一下筆者在vue項目中的常用的工具函數。

          • 識別ie瀏覽器
          /**  
           * 識別ie--淺識別  
           */  
          export const isIe = () => {  
              let explorer = window.navigator.userAgent;  
                  //判斷是否為IE瀏覽器  
              if (explorer.indexOf("MSIE") >= 0) {  
                  return true;  
              }else {  
                  return false  
              }  
          }  
          
          • 顏色16進制轉rgba
          /**  
           * 顏色轉換16進制轉rgba  
           * @param {String} hex  
           * @param {Number} opacity  
           */  
          export function hex2Rgba(hex, opacity) {  
          	if(!hex) hex = "#2c4dae";  
              return "rgba(" + parseInt("0x" + hex.slice(1, 3)) + "," + parseInt("0x" + hex.slice(3, 5)) + "," + parseInt("0x" + hex.slice(5, 7)) + "," + (opacity || "1") + ")";  
          }  
          
          • 去除html標簽
          // 去除html標簽  
          export const htmlSafeStr = (str) => {  
              return str.replace(/<[^>]+>/g, "")  
          }  
          
          • 獲取url參數對象
          /* 獲取url參數 */  
          export const getQueryString = () => {  
              let qs = location.href.split('?')[1] || '',  
                  args = {},  
                  items = qs.length ? qs.split("&") : [];  
                  items.forEach((item,i) => {  
                      let arr = item.split('='),  
                          name = decodeURIComponent(arr[0]),  
                          value = decodeURIComponent(arr[1]);  
                          name.length && (args[name] = value)  
                  })  
              return args;  
          }  
          
          • 解析url參數
          /* 解析url參數 */  
          export const paramsToStringify = (params) => {  
              if(params){  
                  let query = [];  
                  for(let key in params){  
                      query.push(`${key}=${params[key]}`)  
                  }  
                  return `${query.join('&')}`  
              }else{  
                  return ''  
              }  
          }  
          
          • 將數據轉化為數組
          export const toArray = (data) => {  
              return Array.isArray(data) ? data : [data]  
          }  
          
          • 帶參數跳轉url(hash模式)
          /**  
           *  帶參數跳轉url(hash模式)  
           * @param {String} url  
           * @param {Object} params  
           */  
          export const toPage = (url, params) => {  
              if(params){  
                  let query = [];  
                  for(let key in params){  
                      query.push(`${key}=${params[key]}`)  
                  }  
                  window.location.href = `./index.html#/${url}?${query.join('&')}`;  
              }else{  
                  window.location.href = `./index.html#/${url}`;  
              }  
          }  
          
          • 控制字符串顯示,超出指定字數則顯示省略號
          /**  
           * 指定字符串 溢出顯示省略號  
           * @param {String} str  
           * @param {Number} num  
           */  
          export const getSubStringSum = (str = "", num = 1) => {  
              let newStr;  
              if(str){  
                  str = str + '';  
                  if (str.trim().length > num ) {  
                      newStr = str.trim().substring(0, num) + "...";  
                  } else {  
                      newStr = str.trim();  
                  }  
              }else{  
                  newStr = ''  
              }  
              return newStr;  
          }  
          
          • 生成uuid
          /**  
           * 生成uuid  
           * @param {number} len 生成指定長度的uuid  
           * @param {number} radix uuid進制數  
           */  
          export function uuid(len, radix) {  
              let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');  
              let uuid = [], i;  
              radix = radix || chars.length;  
             
              if (len) {  
                for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];  
              } else {  
                let r;  
             
                uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';  
                uuid[14] = '4';  
             
                for (i = 0; i < 36; i++) {  
                  if (!uuid[i]) {  
                    r = 0 | Math.random()*16;  
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];  
                  }  
                }  
              }  
             
              return uuid.join('');  
          }  
          
          • 生成指定格式的時間字符串
          /**  
           * 生成指定格式的時間  
           * @param {*} timeStemp 時間戳  
           * @param {*} flag 格式符號  
           */  
          export function formatTime(timeStemp, flag) {  
              let time = new Date(timeStemp);  
              let timeArr = [time.getFullYear(), time.getMonth() + 1, time.getDate()];  
              return timeArr.join(flag || '/')  
          }  
          

          1.11 如何基于axios二次封裝一個具有請求/響應攔截的http請求

          這個主要是對axios的理解,大家可以學習axios官方文檔,這里給出一個二次封裝的模版:

          import axios from 'axios'  
          import qs from 'qs'  
            
          // 請求攔截  
          axios.interceptors.request.use(config => {  
            // 此處可以封裝一些加載狀態  
            return config  
          }, error => {  
            return Promise.reject(error)  
          })  
            
          // 響應攔截  
          axios.interceptors.response.use(response => {  
            return response  
          }, error => {  
            return Promise.resolve(error.response)  
          })  
            
          function checkStatus (response) {  
            // 此處可以封裝一些加載狀態  
            // 如果http狀態碼正常,則直接返回數據  
            if(response) {  
              if (response.status === 200 || response.status === 304) {  
                return response.data  
                // 如果不需要除了data之外的數據,可以直接 return response.data  
              } else if (response.status === 401) {  
                location.href = '/login';  
              } else {  
                throw response.data  
              }  
            } else {  
              throw {data:'網絡錯誤'}  
            }  
              
          }  
            
          // axios默認參數配置  
          axios.defaults.baseURL = '/api/v0';  
          axios.defaults.timeout = 10000;  
            
          // restful API封裝  
          export default {  
            post (url, data) {  
              return axios({  
                method: 'post',  
                url,  
                data: qs.stringify(data),  
                headers: {  
                  'X-Requested-With': 'XMLHttpRequest',  
                  'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'  
                }  
              }).then(  
                (res) => {  
                  return checkStatus(res)  
                }  
              )  
            },  
            get (url, params) {  
              return axios({  
                method: 'get',  
                url,  
                params, // get 請求時帶的參數  
                headers: {  
                  'X-Requested-With': 'XMLHttpRequest'  
                }  
              }).then(  
                (res) => {  
                  return checkStatus(res)  
                }  
              )  
            },  
            del (url, params) {  
              return axios({  
                method: 'delete',  
                url,  
                params, // get 請求時帶的參數  
                headers: {  
                  'X-Requested-With': 'XMLHttpRequest'  
                }  
              }).then(  
                (res) => {  
                  return checkStatus(res)  
                }  
              )  
            }  
          }  
          

          該模版只是一個大致框架,大家可以細化成業務需求的樣子,該案例提供了restful接口方法,比如get/post/delete/put等。

          1.12 vue常用社區組件,插件

          筆者在做vue項目時為了提高開發效率也會直接用第三方插件,下面整理一下常用的vue社區組件和庫。

          1. UI框架
          • elementUI
          • iview
          • Mint UI 基于 Vue.js 的移動端組件庫
          • Vant 有贊團隊的移動端組件庫
          1. 社區組件
          • Vuetable-2 基于vue的強大的表格組件
          • vue-fa 基于vue的圖標組件庫
          • vue-notification vue優美的信息通知組件
          • vue-progress-path vue個性的路徑進度條組件
          • Vue樹組件,可讓您以美觀和邏輯的方式呈現層次結構的數據
          • vue-social-sharing vue社區分享組件
          • vue-qrcode-reader 一組用于檢測和解碼二維碼的Vue.js組件
          • vue-clipboard2 基于vue的剪切板組件
          • cool-emoji-picker vue表情包組件
          • Vue-tabs-component 強大而美觀的tab組件

          更多組件可以在vue插件社區查看。

          2. vue項目配置經驗總結

          在講完vue項目經驗之后,為了讓大家能獨立負責一個項目,我們還需要知道從0開始搭建項目的步驟,以及通過項目實際情況,自己配置一個符合的項目框架,比如有些公司會采用vue+element+vue+less搭建,有些公司采用vue+iview+vue+sass,或者其他更多的技術棧,所以我們要有把控能力,我們需要熟悉webpack或者vue-cli3腳手架的配置,筆者之前有些過詳細的webpack和vue-cli3搭建自定義項目的文章,這里由于篇幅有限就不一一舉例了。感興趣的朋友可以參考以下兩篇文章:

          • 一張圖教你快速玩轉vue-cli3
          • 用 webpack 4.0 擼單頁/多頁腳手架 (jquery, react, vue, typescript)

          3. vue組件設計經驗總結

          ?

          組件系統是 Vue 的另一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可復用的組件構建大型應用。幾乎任意類型的應用界面都可以抽象為一個組件樹。在一個大型應用中,有必要將整個應用程序劃分為組件,以使開發更易管理。

          ?

          對于一個基礎組件來說,我們該如何下手去設計呢?首先筆者覺得應該先從需求和功能入手,先劃分好組件的功能邊界,組件能做什么,理清這些之后再開始寫組件。

          如上圖組件的一個抽象,我們無論如何記住的第一步就是先理清需求在去著手開發,這樣可以避免大量效率的丟失。在上圖中我們要注意組件的解耦合,每個組件都負責特定的功能或者展現,這樣對組件后期維護性和擴展性有非常大的幫助。筆者總結了一下組件設計的注意事項:

          • 確定組件設計的邊界和功能定位
          • 組件盡量遵循單一職責原理,用組合代替功能的雜糅
          • 組件屬性暴露要適度,不可過渡暴露屬性
          • 組件封裝要考慮可重用,可組合,可配置
          • 做好組件類型設計的劃分(展示型組件,錄入型組件,基礎組件, 布局組件,反饋型組件,業務組件等)

          筆者拿之前在開源社區發布的一個文件上傳組件為例子來說明舉例,代碼如下:

          <template>  
              <div>  
                  <a-upload  
                      :action="action"  
                      listType="picture-card"  
                      :fileList="fileList"  
                      @preview="handlePreview"  
                      @change="handleChange"  
                      :remove="delFile"  
                      :data="data"  
                      >  
                      <template v-if="!fileList.length && defaultValue">  
                          <img :src="defaultValue" alt="" style="width: 100%">  
                      </template>  
                      <template v-else>  
                          <div v-if="fileList.length < 2">  
                              <a-icon type="plus" />  
                              <div class="ant-upload-text">上傳</div>  
                          </div>  
                      </template>  
                  </a-upload>  
                  <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">  
                      <img alt="example" style="width: 100%" :src="previewImage" />  
                  </a-modal>  
              </div>     
          </template>  
            
          <script lang="ts">  
          import { Component, Vue, Prop } from 'vue-property-decorator';  
          @Component  
          export default class Upload extends Vue {  
            @Prop({ default: 'https://www.mocky.io/v2/5cc8019d300000980a055e76' })  
            action!: string;  
            
            @Prop()  
            defaultValue: string;  
            
            @Prop()  
            data: object;  
            
            @Prop({ default: function() {} })  
            onFileDel: any;  
            
            @Prop({ default: function() {} })  
            onFileChange: any;  
            
            public previewVisible: boolean =  false;  
            public previewImage: string =  '';  
            public fileList: object[] = [];  
            
            // 預覽圖片  
            public handlePreview(file: any) {  
              this.previewImage = file.url || file.thumbUrl;  
              this.previewVisible = true;  
            }  
              
            // 刪除文件和回調  
            public delFile(file: any) {  
              this.fileList = [];  
              this.onFileDel();  
            }  
            
            // 文件上傳變化的處理函數  
            public handleChange({ file }: any) {  
              this.fileList = [file];  
              if(file.status === 'done') {  
                this.onFileChange(file.response.url);  
              } else if(file.status === 'error') {  
                this.$message.error(file.response.msg)  
              }  
            }  
            // 取消預覽  
            public handleCancel() {  
                this.previewVisible = false;  
            }  
          }  
          </script>  
          

          以上文件上傳預覽采用的是ts來實現,但設計思路都是一致的,大家可以參考交流一下。 關于如何設計一個健壯的組件,筆者也寫過相關文章,大致思想都好似一樣的,可以參考一下:

          • 《精通react/vue組件設計》之快速實現一個可定制的進度條組件
          • 《精通react/vue組件設計》之用純css打造類materialUI的按鈕點擊動畫并封裝成react組件
          • 3分鐘教你用原生js實現具有進度監聽的文件上傳預覽組件

          組件的設計思想和方法與具體框架無關,所以組件設計的核心是方法論,我們只有在項目中不斷總結和抽象,才能對組件設計有更深刻的理解。

          4. vue項目架構與服務化探索

          這里是筆者總結的一套思維導圖:

          有點微前端架構的感覺,但是還有很多細節需要考慮。此架構適用于不同子系統獨立部署和開發的環境, 也不需要考慮具體的技術棧選擇,相同的框架采用同一套自建組件庫來達到組件的復用,這里提倡項目開始設計時就考慮組件化和模塊化,做出功能的最大的拆分和去耦合。筆者后面會單獨花時間總結微前端架構具體的一些設計思路和落地方案,感興趣的可以一起探討交流。


          上一篇:JavaScript定時器
          下一篇:HTML基礎的用法
          主站蜘蛛池模板: 一区二区三区无码高清视频| 亚洲AV综合色区无码一区爱AV| 日韩av片无码一区二区不卡电影| 激情内射亚州一区二区三区爱妻| 精品一区二区三区在线播放视频 | 国产激情无码一区二区app| 女同一区二区在线观看| 亚洲日韩一区二区三区| 亚洲丰满熟女一区二区哦| 精品一区二区三区影院在线午夜| 日本亚洲成高清一区二区三区| 国产一区二区视频在线观看| 精品视频一区二区三区四区| 亚洲视频一区二区| 美女福利视频一区| 在线视频一区二区三区| 日韩熟女精品一区二区三区| 亚洲国产AV无码一区二区三区| 色欲AV无码一区二区三区| 亚洲欧洲精品一区二区三区| 亚洲sm另类一区二区三区| 一区二区三区波多野结衣| 中文字幕一区在线| 亚洲国模精品一区| 国产精品电影一区二区三区| 天天看高清无码一区二区三区| 国产爆乳无码一区二区麻豆| 韩国福利影视一区二区三区| 精品国产AV无码一区二区三区| 91精品一区二区| 内射少妇一区27P| 视频一区在线免费观看| 无码丰满熟妇一区二区| 另类一区二区三区| 日韩人妻无码一区二区三区久久| 日本精品高清一区二区2021| 国产成人综合一区精品| 在线精品一区二区三区电影 | 国产高清在线精品一区| 国产精品亚洲一区二区无码 | 国产日韩综合一区二区性色AV|