<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          uni-app提交表單到后端,接收表單數據

          seo達人

          要想接收表單數據,首先要在表單進行數據的綁定,我們可以使用v-model="keyword"進行綁定。


          然后在js獲取這個綁定的值。


          index.vue


          <template>

          <view>

             <view class="search-con">

                     <view class="form-con">

                     <form class="search-form">

                         <input type="text" v-model="keyword" @tap="showsearchbtn" focus="true"/>

                         <button form-type="submit" hover-class='none' @tap="keyword">提交</button>  

                     </form>

                     </view>

             </view>

          </view>

          <template>

          js


          <script>

          export default {

             data() {

                 return {


                     }

                 },

                 methods: {

                     keyword(e){

                         // 獲取表單值

                         let kw = this.keyword;

                         console.log(kw);

                     }

                 }

             }

          </script>

          Author:TANKING

          Web:http://www.likeyun.cn

          Date:2020-8-13

          WeChat:face6009

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          滴滴出行小程序I18n最佳實踐

          seo達人

          背景

          I18n = Internationalization,國際化,因為單詞由首末字符i/n和中間18個字母組成,簡稱i18n。對程序來說,就是要在不修改內部代碼的情況下,能根據不同語言及地區顯示相應的界面,以支持不同語言的人順利使用程序。

          業務背景

          互聯網行業進入下半場,精細化運營是關鍵。多語言支持能讓產品更好地服務境內的其他語言用戶,也為產品出海打下基礎,隨著 WeChat/Alipay 的全球化,你的小程序是否做好準備了呢?

          4月初,滴滴出行小程序團隊接到支持英文版的需求,預計上線時間為6月上旬。當前滴滴出行小程序集成的眾多業務線和各種公共庫,展示給用戶的有前端硬編碼的靜態文本和服務端下發的文案,都要同步接入多語言。考慮到小程序當前的體量,光文本收集、語料翻譯、npm package 支持,聯調,測試,溝通成本等等,并且前端開發只投入1.5人力的情況下,時間是蠻緊迫的,但是我們抗住了壓力,最終英文版滴滴出行小程序如期上線,截止目前運行穩定,用戶反饋良好,得到了超出預期的收益。

          當然這一切得益于各團隊同學的工作,和各團隊的通力配合,更得益于部門技術團隊 Mpx框架優雅的多語言能力支持。劃重點來咯,所謂工欲善其事必先利其器,如果你的公司業務需要開發小程序,也需要接入多語言,那么請搬好小板凳,我們來看一下小程序框架 Mpx 是如何優雅支持多語言能力。相信看完這篇,可以幫助你認識 Mpx(https://github.com/didi/mpx) ,加深對框架的理解,最終利用 Mpx 框架迭代小程序,年終獎多出那部分可以打賞一下作者,買杯咖啡哈(偷笑.jpg)

          以下是滴滴出行小程序的中英文版本對比:

          滴滴出行微信小程序i18n

          也歡迎大家在微信/支付寶里搜索滴滴出行小程序,實際使用感受下。PS:切換語言的方法是,打開小程序,點擊左上角用戶頭像,進入側邊欄設置頁面,點擊切換中英文即可體驗。

          技術背景

          在上述業務背景下,Mpx 框架——滴滴自研的專注提升小程序開發體驗的增強型小程序框架,內建 i18n 能力便提上日程。

          與 WEB 不同,小程序(本文以微信小程序為例)運行環境采用雙線程架構設計,渲染層的界面使用 WebView 進行渲染,邏輯層采用 JSCore 線程運行 JS腳本。邏輯層數據改變,通過 setData 將數據轉發到 Native(微信客戶端),Native 再將數據轉發到渲染層,以此更新頁面。由于線程間通信成本較高,實際項目開發時需要控制頻次和數量。另外小程序的渲染層不支持運行 JS ,一些如事件處理等操作無法在渲染層實現,因此微信官方提供了一套腳本語言 WXS ,結合 WXML ,可以構建出頁面的結構(不了解 WXS ?戳這里)。

          基于小程序的雙線程架構設計,實現 i18n 存在一些技術上的難點與挑戰,由于 Mpx 框架早期構建起來的強大基礎,最終得以優雅支持多語言能力,實現了和vue-i18n 基本一致的使用體驗。

          使用

          在使用上,Mpx 支持 i18n 能力提供的 API 與 vue-i18n 大體對齊,用法上也基本一致。

          模板中使用 i18n

          編譯階段通過用戶配置的 i18n 字典,結合框架內建的翻譯函數通過 wxs-i18n-loader 合成為可執行的 WXS 翻譯函數,并自動注入到有翻譯函數調用的模板中,具體調用方式如下圖。

          // mpx文件 <template> <view> <view>{{ $t('message.hello', { msg: 'hello' })}}</view> 

          <!-- formattedDatetime計算屬性,可基于locale變更響應刷新 --> <view>{{formattedDatetime}}</view> </view> </template>

          JS 中使用 i18n

          通過框架提供的 wxs2js 能力,將 WXS 翻譯函數轉換為 JS 模塊注入到 JS 運行時,使運行時環境中也能夠調用翻譯函數。

          // mpx文件 <script> import mpx, { createComponent } from '@mpxjs/core' createComponent({ 
          

          ready () { // js中使用 console.log(this.$t('message.hello', { msg: 'hello' }))

          // 局部locale變更,生效范圍為當前組件內 this.$i18n.locale = 'en-US' setTimeout(() =>

          { // 全局locale變更,生效范圍為項目全局 mpx.i18n.locale = 'zh-CN' }, 10000)

          }, computed: { formattedDatetime () { return this.$d(new Date(), 'long') } } }) </script>

          定義 i18n 字典

          項目構建時傳入 i18n 配置對象,主要包括語言字典和默認語言類型。

          new MpxWebpackPlugin({ i18n: { locale: 'en-US',

          // messages既可以通過對象字面量傳入,也可以通過messagesPath指定一個js模塊路徑,

          在該模塊中定義配置并導出,dateTimeFormats/dateTimeFormatsPath和numberFormats/numberFormatsPath同理

          messages: { 'en-US': { message: { hello: '{msg} world' }

          }, 'zh-CN': { message: { hello: '{msg} 世界' } } }, // messagesPath: path.resolve(__dirname, '../src/i18n.js') } })

          如果是通過 Mpx 提供的 cli 工具生成的項目,這部分配置會在 mpx.conf.js 文件中,不光可以直接內聯寫在該文件中,也可以指定語言包的路徑。

          以上,Mpx 的 i18n 方案接入成本低,使用優雅,體驗優秀。直觀感受可參考下面 mpx i18n demo :https://github.com/didi/mpx/t...

          方案

          Mpx框架的 i18n 支持幾乎完全實現了 vue-i18n 的全部能力,下面我們來詳細說明 Mpx 框架 i18n 能力的具體實現。

          方案探索

          基于小程序運行環境的雙線程架構,我們嘗試了不同方案,具體探索過程如下:

          方案一:基于 Mpx 框架已提供的數據增強能力 computed 計算屬性,來支持 i18n 。該方案與 uniapp 的實現思路相似(后文會進行對比分析),存在一定不足,包括線程通信帶來的性能開銷和for循環場景下的處理較復雜等,最終放棄。
          方案二:基于 WXS + JS 支持 i18n 適配。通過視圖層注入 WXS,將 WXS 語法轉換為 JS 后注入到邏輯層,這樣視圖層和邏輯層均可實現 i18n 適配,并且在一定程度上有效減少兩個線程間的通信耗時,提高性能。

          從性能和合理性上考慮,我們最終采用了方案二進行 Mpx 的 i18n 方案實現。

          mpx-i18n內部流程示意圖

          Mpx i18n 架構設計圖

          由于各大小程序平臺上,WXS 語法和使用均存在較大差異,因此該方案實現過程中也存在一些技術上的難點,這些難點基于 Mpx 框架的早期構建起來的跨平臺能力也一一得以攻克,具體如下。

          實現難點

          WXS 在模板中運行的跨平臺處理

          WXS 是運行在視圖層中的 JS,可以減少與邏輯層通信耗時,提高性能。因此 Mpx 框架在迭代初期便已支持在模板和 JS 運行環境使用 WXS 語言,并且針對小程序跨平臺 WXS 語法進行抹平。
          在模板中,Mpx 自定義一個 webpack chunk template,以微信 WXS 作為 DSL,利用 babylon 將注入的 WXS 轉化成 ast,然后遍歷 ast 節點,抹平各大平臺對 WXS 語法的處理差異,輸出各平臺可以識別的類 WXS 文件。目前主要支持微信(WXS)、支付寶(sjs)、百度(filter)、QQ(qs)、頭條(sjs)等小程序平臺。

          WXS 在邏輯層運行的跨平臺處理

          WXS 與 JavaScript 是不同的語言,有自己的語法,并不和 JavaScript 一致。并且 WXS 的運行環境和其他 JavaScript 代碼是隔離的,WXS 中不能調用其他 JavaScript 文件中定義的函數,也不能調用小程序提供的API。
          因此在邏輯層,Mpx 將注入的 WXS 語法轉化為 JS,通過 webpack 注入到當前模塊。例如 WXS 全局方法 getRegExp/getDate 在 JS 中是無法調用的,Mpx將它們分別轉化成 JS 模塊,再通過 webpack addVariable 將模塊注入到 bundle.js 中。
          同理,Mpx 會將編譯時注入的 i18n wxs 翻譯函數和 i18n 配置對象掛載到全局 global 對象上,利用 mixin 混入到頁面組件,并監聽 i18n 配置對象,這樣JS和模板中即可直接調用 i18n 翻譯函數,實現數據響應。

          以上便是 Mpx 框架在小程序中支持 i18n 能力的技術細節,由于 WXS 是可以在視圖層執行的類 JS 語法的一門語言,這樣就減少了小程序邏輯層和視圖層的通信耗時,提升性能。但是由于實現依賴類 WXS 能力,以及 WXS 執行環境的限制,目前模板上可直接使用的翻譯函數包括 $t/$tc/$te ,如果需要格式化數字或日期可以使用對應的翻譯函數在 JS 中 Mpx 提供的計算屬性中實現。

          輸出 web 時使用 i18n

          Mpx同時還支持轉換產出H5,而 Mpx 提供的 i18n 能力在使用上與 vue-i18n 基本一致,輸出 web 時框架會自動引入 vue-i18n,并使用當前的 Mpx i18n 配置信息對其進行初始化,用戶無需進行任何更改,即可輸出和小程序表現完全一致的 i18n web 項目。

          對比

          上面分析了 Mpx 框架的 i18n 方案的技術細節,我們來看下和其他方案的對比,主要是和 uniapp - 基于 Vue 編寫小程序的方案,和微信官方的方案,兩者提供的 i18n 支持與Mpx的對比有何優劣。

          uniapp的方案

          uniapp 提供了對 i18n 能力的支持,是直接引入vue-i18n。但小程序中無法在模板上調用 JS 方法,本質上是利用計算屬性 Computed 轉換好語言,然后利用模板插值在小程序模板中使用。

          模板中:
          <view>{{ message.hello }}</view>

          JS里需要寫:

           computed: {  
              message () { return { hello: this.$t('message.hello') }
              }
            }

          因此該方案存在一個性能問題,最終的渲染層所看到的文本還是通過 setData 跨線程通信完成,這樣就會導致線程間通信增多,性能開銷較大。

          并且,早期這種形式使用成本較高,后來 uniapp 也針對其做過優化,實現了可以在模板上寫 $t() 的能力,使用上方便了不少。

          這個 t() 的實現是在編譯時候識別到t 就自動替換,幫你替換成一個 uniapp 的 computed 數據,因此數據部分還是和之前一樣要維護兩份。尤其是模板上的for循環,即使 for 里只有一個數據要被轉換,整個列表都要被替換成一個計算屬性,在線程間通信時進一步加大了性能開銷。

          微信官方的方案

          微信小程序本身也提供了一個 i18n 的方案,倉庫地址是:wechat-miniprogram/miniprogram-i18n 。

          這個方案從 i18n 本身的實現來講和Mpx框架的設計是類似的,也是基于 WXS 實現(英雄所見略同?。5驗橹苓吪涮咨蠜]有完整的體系,整體使用體驗上就也略遜于基于Mpx框架來開發支持 i18n 的國際化小程序了。

          主要的點就是,官方提供的方案,要基于 gulp 工具進行一次額外構建,同時在JS中使用時候還要額外引入一個 behavior 去讓JS中也可以使用翻譯能力。

          而Mpx框架通過一次統一的Webpack構建產出完整的內容,用戶無需擔心語言包更新后忘記重新構建,在JS中使用的時候不光更方便,而且語言信息還是個響應式的,任何組件都可以很方便地監聽語言值的變化去做一些其他的事情。

          最后,Mpx的 i18n 方案對比微信官方的方案還有個巨大的優點,結合Mpx的跨平臺能力,能實現均以這個方案,一套代碼產出支持微信/支付寶/百度/QQ/頭條多個平臺的支持 i18n 的小程序。

          總結

          Mpx 框架專注小程序開發,期望為開發者提供最舒適的開發體驗,有眾多優秀的功能特性,幫助開發者提效。本文介紹的是其內置的 i18n 能力,通過對比分析得出相比其他框架方案在使用成本和性能等方面有明顯的優勢,歡迎各位有相關需求的同學進行體驗嘗試。

          未來 Mpx 還會持續迭代優化,提供更多更好的能力幫助小程序開發者提效。在使用過程中遇到任何問題,歡迎大家在 Git 上提 issue,團隊成員會及時響應。同時也鼓勵大家一起為開源社區做貢獻,參與到 Mpx 共建中來,為小程序技術發展添磚加瓦。

          Git地址 [https://github.com/didi/mpx]
          Mpx文檔 [https://mpxjs.cn/]

          歡迎技術交流與反饋,順便star一下鼓勵開源項目貢獻者,我們將持續發力貢獻社區。

          附:以往Mpx文章鏈接
          滴滴開源小程序框架Mpx - https://mpxjs.cn/articles/1.0.html
          滴滴小程序框架Mpx發布2.0,支持小程序跨平臺開發,可直接轉換已有微信小程序 - https://mpxjs.cn/articles/2.0.html
          小程序開發者,為什么你應該嘗試下MPX - https://mpxjs.cn/articles/mpx1.html
          Mpx 小程序框架技術揭秘 - https://mpxjs.cn/articles/mpx2.html

          滴滴出行小程序體積優化實踐 - https://mpxjs.cn/articles/size-control.html

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          JavaScript專題之深淺拷貝(系列五)[轉載]

          前端達人

          JavaScript專題之深淺拷貝

          了解拷貝背后的過程,避免不必要的錯誤,Js專題系列之深淺拷貝,我們一起加油~

          目錄

          一、拷貝示例

          當我們在操作數據之前,可能會遇到這樣的情況:

          1. 會經常改動一組數據,但可能會用到原始數據
          2. 我需要兩組一樣的數據,但我不希望改動一個另一個隨之改動
          3. 我需要對數據操作前后進行對比

          當我們遇到類似需要場景時,首先想到的就是拷貝它,殊不知拷貝也大有學問哦~

          下面簡單的例子,你是否覺得熟悉?

          1.1 基本類型拷貝示例
          var str = 'How are you'; var newStr = str; newStr = 10 console.log(str); // How are you console.log(newStr); // 10 
          

          大家都能想到,字符串是基本類型,它的值保存在棧中,在對它進行拷貝時,其實是為新變量開辟了新的空間。 strnewStr就好比兩個一模一樣的房間,布局一致卻毫無關聯。

          1.2 引用類型拷貝示例
          var data = [1, 2, 3, 4, 5]; var newData = data; newData[0] = 100; console.log(data[0]); // 100 console.log(newData[0]); // 100 
          

          類似的代碼段,但這次我們使用數組這個引用類型舉例,你會發現修改賦值后的數據,原始數據也跟著改變了,這顯然不滿足我們的需要。本篇文章就來聊一聊引用數據拷貝的學問。

          如果大家對Js的數據類型存在著疑問,不妨看看《JavaScript中的基本數據類型》

          在這里插入圖片描述

          二、淺拷貝

          拷貝的劃分都是針對引用類型來討論的,淺拷貝——顧名思義,淺拷貝就是“淺層拷貝”,實際上只做了表面功夫:

          var arr = [1, 2, 3, 4]; var newArr = arr; console.log(arr, newArr); // [1,2,3,4] [1,2,3,4] newArr[0] = 100; console.log(arr, newArr) // [100,2,3,4] [100,2,3,4] 
          

          不發生事情(操作)還好,一旦對新數組進行了操作,兩個變量中所保存的數據都會發生改變。

          發生這類情況的原因也是因為引用類型的基本特性:

          • 存儲在變量處的值是一個指針(point),指向存儲對象的內存地址。賦值給新變量相當于配了一把新鑰匙,房間并沒有換。

          數組中的slice和concat都會返回一個新數組,我們一起來試一下:

          var arr = [1,2,3,4]; var res = arr.slice(); // 或者 res = arr.concat() res[0] = 100; console.log(arr); // [1,2,3,4] 
          

          這個問題這么快就解決了?雖然對這一層數據進行了這樣的的處理后,確實解決了問題,但!

          var arr = [ { age: 23 }, [1,2,3,4] ]; var newArr = arr.concat(); arr[0].age = 18; arr[1][0] = 100; console.log(arr) // [ {age: 18}, [100,2,3,4] ] console.log(newArr) // [ {age: 18}, [100,2,3,4] ] 
          

          果然事情沒有那么簡單,這也是因為數據類型的不同。

          S 不允許我們直接操作內存中的地址,也就是說不能操作對象的內存空間,所以,我們對對象的操作都只是在操作它的引用而已。

          既然淺拷貝達不到我們的要求,本著效率的原則,我們找找有沒有幫助我們實現深拷貝的方法。

          在這里插入圖片描述

          三、深拷貝的方法?

          數據的方法失敗了,還有沒有其他辦法?我們需要實現真正意義上的拷貝出獨立的數據。

          3.1 JSON

          這里我們利用JSON的兩個方法,JSON.stringify(),JSON.parse()來實現最簡潔的深拷貝

          var arr = ['str', 1, true, [1, 2], {age: 23}] var newArr = JSON.parse( JSON.stringify(arr) ); newArr[3][0] = 100; console.log(arr); // ['str', 1, true, [1, 2], {age: 23}] console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}] 
          

          這個方法應該是實現深拷貝最簡潔的方法,但是,它仍然存在問題,我們先來看看剛才都做了些什么:

          1. 定義一個包含都過類型的數組arr
          2. JSON.stringify(arr), 將一個 JavaScript 對象或值轉換為 JSON 字符串
          3. JSON.parse(xxx), 方法用來解析JSON字符串,構造由字符串描述的值或對象

          理解:

          我們可以理解為,將原始數據轉換為新字符串,再通過新字符串還原為一個新對象,這中改變數據類型的方式,間接的繞過了拷貝對象引用的過程,也就談不上影響原始數據。

          限制:

          這種方式成立的根本就是保證數據在“中轉”時的完整性,而JSON.stringify()將值轉換為相應的JSON格式時也有缺陷:

          • undefined、任意的函數以及 symbol 值,在序列化過程中會被忽略(出現在非數組對象的屬性值中時)或者被轉換成 null(出現在數組中時)。
          • 函數、undefined 被單獨轉換時,會返回 undefined,
            • 如JSON.stringify(function(){})
            • JSON.stringify(undefined)
          • 對包含循環引用的對象(對象之間相互引用,形成無限循環)執行此方法,會拋出錯誤。
          • NaN 和 Infinity 格式的數值及 null 都會被當做 null。
          • 其他類型的對象,包括 Map/Set/WeakMap/WeakSet,僅會序列化可枚舉的屬性。

          所以當我們拷貝函數、undefined等stringify轉換有問題的數據時,就會出錯,我們在實際開發中也要結合實際情況使用。

          舉一反三:

          既然是通過改變數據類型來繞過拷貝引用這一過程,那么單純的數組深拷貝是不是可以通過現有的幾個API來實現呢?

          var arr = [1,2,3]; var newArr = arr.toString().split(',').map(item => Number(item)) newArr[0] = 100; console.log(arr); // [1,2,3] console.log(newArr); // [100,2,3] 
          

          注意,此時僅能對包含純數字的數組進行深拷貝,因為:

          1. toString無法正確的處理對象和函數
          2. Number無法處理 false、undefined等數據類型

          但我愿稱它為純數字數組深拷貝!

          在這里插入圖片描述

          3.2 Object.assign()

          有的人會認為Object.assign(),可以做到深拷貝,我們來看一下

          var obj = {a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj) newObj.a = 100; newObj.b.c = 200; console.log(obj); // {a: 1, b: { c: 200 } } console.log(newObj) // {a: 100, b: { c: 200 } } 
          

          神奇,第一層屬性沒有改變,但第二層卻同步改變了,這是為什么呢?

          因為 Object.assign()拷貝的是(可枚舉)屬性值。

          假如源值是一個對象的引用,它僅僅會復制其引用值。MDN傳送門

          四、自己實現深淺拷貝

          既然現有的方法無法實現深拷貝,不妨我們自己來實現一個吧~

          4.1 淺拷貝

          我們只需要將所有屬性即其嵌套屬性原封不動的復制給新變量一份即可,拋開現有的方法,我們應該怎么做呢?

          var shallowCopy = function(obj) { if (typeof obj !== 'object') return; // 根據obj的類型判斷是新建一個數組還是對象 var newObj = obj instanceof Array ? [] : {}; // 遍歷obj,并且判斷是obj的屬性才拷貝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; } 
          

          我們只需要將所有屬性的引用拷貝一份即可~

          4.2 深拷貝

          相信大家在實現深拷貝的時候都會想到遞歸,同樣是判斷屬性值,但如果當前類型為object則證明需要繼續遞歸,直到最后

          var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; } 
          

          我們用白話來解釋一下deepCopy都做了什么

          const obj = [1, { a: 1, b: { name: '余光'} } ]; const resObj = deepCopy(obj); 
          • 讀取 obj,創建 第一個newObj
            • 判斷類型為 []
            • key為 0 (for in 以任意順序遍歷,我們假定按正常循序遍歷)
            • 判斷不是引用類型,直接復制
            • key為 1
            • 判斷是引用類型
            • 進入遞歸,重新走了一遍剛才的流程,只不過讀取的是obj[1]

          另外請注意遞歸的方式雖然可以深拷貝,但是在性能上肯定不如淺拷貝,大家還是需要結合實際情況來選擇。




          作者: 

          轉自:https://blog.csdn.net/jbj6568839z/article/details/107964274?utm_medium=distribute.pc_category.none-task-blog-hot-4.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-4.nonecase&request_id=

          最詳細完整的flex彈性布局

          前端達人

          初了解

          在學習彈性布局之前首先就要明白其概念
          flex 就是flexible box的縮寫,意為彈性布局,用來為盒裝模型提供最大的靈活性
          任何一個容器都可以指定為flex布局

          .box{ display: flex; } 
          

          行內元素當然也可以使用flex布局

          .box{ display: inline-flex; } 
          

          Webkit 內核的瀏覽器,必須加上-webkit前綴。

          .box{ display: -webkit-flex; /* Safari */ display: flex; } 
          

          注意:設為 Flex 布局以后,子元素的float、clear和vertical-align屬性將失效。

          基本概念

          采用 Flex 布局的元素,稱為 Flex 容器(flex container),簡稱"容器"。它的所有子元素自動成為容器成員,稱為 Flex 項目(flex item),簡稱"項目"。
          在這里插入圖片描述
          容器默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。主軸的開始位置(與邊框的交叉點)叫做main start,結束位置叫做main end;交叉軸的開始位置叫做cross start,結束位置叫做cross end。
          項目默認沿主軸排列。單個項目占據的主軸空間叫做main size,占據的交叉軸空間叫做cross size。
          以上這些基礎概念,請務必牢記,下面說屬性時,不再重復說明!

          屬性

          容器屬性(container)

          • flex-direction
          • justify-content
          • align-items
          • flex-wrap
          • align-content
          • flex-flow

          1.flex-direction

           flex items默認都是沿著main axis(主軸)從main start 開始往main end方向排布
              flex-direction決定了main axis的方向,有四個取值row(默認值)、row-reverse、column、column-reverse 
          
          .box { flex-direction: row | row-reverse | column | column-reverse; } 
          

          row(默認值):主軸為水平方向,起點在左端。
          row-reverse:主軸為水平方向,起點在右端。
          column:主軸為垂直方向,起點在上沿。
          column-reverse:主軸為垂直方向,起點在下沿。

          2.justify-content

           justify-content決定了flex item在main axis上的對齊方式
          
              flex-start(默認值):與main start對齊
              flex-end:與main end對齊
              center:居中
              space-between:flex items 之間的距離相等,與main start、main end兩端對齊
              space-evently: flex items 之間的距離相等,flex items與main start 、main end 之間的距離等于flex items之間的距離
              space-around :flex items 之間的距離相等,flex items與main start 、main end 之間的距離等于flex items之間的距離的一半 
          

          這個屬性的目的主要就是為了排列main axis的item位置
          在這里插入圖片描述

          在這里插入圖片描述
          當然,這些屬性你可以自己嘗試一下,這里就不再一一嘗試了,但是注意,這些都是容器的屬性,要寫在容器的css中!

          3.align-items

           決定flex items在cross axis上的對齊方式
          
              normal:在彈性布局中,效果和stretch一樣
              stretch:前提是items不設置高度,當flex items 在cross axis 方向的size為auto時,會自動拉伸至填充flex container(或者換句話說:如果項目未設置高度或設為auto,將占滿整個容器的高度。)
              flex-satrt:與cross start 對齊
              flex-end:與cross end 對齊
              center:居中對齊
              baseline:與基準線對齊 
          

          4.flex-wrap

           決定了flex container 是單行還是多行
              nowrap(默認):單行
              warp:多行
              //這個比較少用
              wrap-reverse:多行(對比wrap,cross start 與cross end相反) 
          

          默認情況下,項目都排在一條線(又稱"軸線")上。flex-wrap屬性定義,如果一條軸線排不下,如何換行。

          5 align-content

           決定了多行flex items 在cross axis的對齊方式 用法與justify-content相似 一個是橫軸。一個控制豎軸
              stretch(默認值):與align-items的stretch類似,當items有高度的時候,無效果
              flex-start:與cross start 對齊
              flex-end :與cross end 對齊
              center:居中對齊
              space-between:flex items 之間的距離相等,與cross start、cross end兩端對齊
              space-evently: flex items 之間的距離相等,flex items與cross start 、cross end 之間的距離等于flex items之間的距離
              space-around :flex items 之間的距離相等,flex items與cross start 、cross end 之間的距離等于flex items之間的距離的一半 
          

          6 flex-flow 是flex-direction與flex-wrap的簡寫

          也就是說,當你使用這個屬性的時候,你可以使用上述兩個的屬性值,例如:flex-flow: row wrap;(水平排列,多行顯示)

          flex 項目屬性(item屬性)

          • order
          • flex-grow
          • flex-shrink
          • flex-basis
          • align-self
          • flex

          1 order

           order 決定flex items的排布順序  (用的不多)
              可以設置為任意整數(正整數、負整數、0),值越小越排在前面
              默認值為0 
          

          這個屬性了解即可,說實話沒怎么用過

          2 align-self

           可以通過align-self 覆蓋flex container 設置的align-items
              auto(默認值):遵從flex container的align-items設置
              stretch、flex-start、flex-end、center、baseline效果與align-items一致 
          

          相當于繼承父元素的align-items屬性,如果沒有父元素,則等同于stretch。

          3 flex-grow

           決定了flex items如何擴展
              可以設置為任意非父數字(小數,整數 0),默認為0
              當flex container 在main axis方向上有剩余得size時,flex-grow屬性才會有效
          
              如果所有flex items 的flex-grow 綜合sum不超過1,這直接乘以剩余size就是擴展大小、
              如果超過1 擴展size=剩余size*flex-grow/sum 
          

          flex-grow屬性定義項目的放大比例,默認為0,即如果存在剩余空間,也不放大。

          4 flex-shrink

          flex-shrink (shrink 縮小,收縮)與flex-grow相似,一個擴展,一個伸縮 
              可以設置為任意非父數字(小數,整數 0),默認為1
              當flex items在main axis 方向上超過了flex container 的size flex-shrink屬性才會生效、
              如果所有flex items 的flex-shrink 總和sum超過1,每個flex item 收縮的size為:
                  flex item 超出flex container 的size*收縮比例/每個flex items 的收縮比例之和
              如果sum不超過1,每個flex item 收縮的size為:
                  size = 超出的size * flex-shrink值
              flex items收縮后的最終size不能小于min-width\min-height 
          

          有擴大自然就會有縮小,flex-shrink屬性定義了項目的縮小比例,默認為1,即如果空間不足,該項目將縮小。
          如果所有項目的flex-shrink屬性都為1,當空間不足時,都將等比例縮小。如果一個項目的flex-shrink屬性為0,其他項目都為1,則空間不足時,前者不縮小。具體的可以自己動手嘗試一下哦,最后將會給出一個骰子布局的案例!

          5 flex-basis

          用來設置flex items 在 main axis方向上的base size
              默認為auto,可以設置具體的寬度數值
          
              決定flex items最終base size 的因素,優先級從高到低
                  max-width\max-height\min-width\min-height
                  flex-basis
                  width\height
                  內容本身的size 
          

          flex-basis屬性定義了在分配多余空間之前,項目占據的主軸空間(main size)。瀏覽器根據這個屬性,計算主軸是否有多余空間。它的默認值為auto,即項目(item)的本來大小。也可以設置跟width,height一樣的寬高,表示item將占據固定的空間!

          6 flex

          flex 是flex-grow || flex-shink||flex-basis的簡寫
          可以指定1 2 3個值 依次按照上述順序!默認值為 0 1 auto 
          
          .item { flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] } 
          

          注意:

          1. 該屬性的默認值為 0 1 auto(注意順序),后兩個屬性可選
          2. 該屬性有兩個快捷值:auto (1 1 auto) 和 none (0 0 auto)。
          3. 如果需要這三個屬性的時候,建議使用flex,而不是單獨的三個分離的屬性,因為瀏覽器會推算相關值

          骰子布局實踐

          光說不練假把式,手撕代碼真功夫!
          下面利用flex寫了幾個骰子布局,可以參考一下!

           
          
          <!DOCTYPE html>
          <html>
              <head>
                  <meta charset="utf-8">
                  <title></title>
                  <style type="text/css">
                      #container{
                          background-color: #CCCCCC;
                          height: 600px;
                          width: 500px;
                          /* flex */
                          display: flex;
                          justify-content: space-evenly;
                          align-items: center;
                      }
                      .item{
                          background-color: yellow;
                          width: 100px;
                          height: 100px;
          
                      }
                      /* 單點 */
                      .one{
                          /* 對點使用flex布局 */
                          display: flex;
                          justify-content: center;
                          align-items: center;
                      }
                      /* 點 */
                      .item-one{
                          display: block;
                          height: 20px;
                          width: 20px;
                          background-color: #1890FF;
                          border-radius: 50%;
                      }
                      /* 三點 */
                      .two{
          
                          display: flex;
                          justify-content: space-between;
                      }
                      .two span{
                          margin: 2px;
                          display: block;
                          height: 20px;
                          width: 20px;
                          border-radius: 50%;
                          background-color: #1890FF;
                      }
                      .two2{
                          align-self: center;
                      }
                      .two3{
                          align-self: flex-end;
                      }
                      /* 五點 */
                      .three{
                          display: flex;
                          justify-content: space-around;
                      }
                      .three span{
                          display: block;
                          height: 20px;
                          width: 20px;
                          border-radius: 50%;
                          background-color: #1890FF;
                      }
                      #three_one, #three_three{
                          padding: 2px;
                          display: flex;
                          flex-direction: column;
                          justify-content: space-between;
                      }
                      #three_two{
                          display: flex;
                          flex-direction: column;
                          justify-content: center;
                      }
                      /* 六點 */
                      .four{
                          display: flex;
                          justify-content: space-around;
                      }
                      .four span{
                          display: block;
                          height: 20px;
                          width: 20px;
                          border-radius: 50%;
                          background-color: #1890FF;
                      }
                      #four1,#four2{
                          padding: 2px;
                          display: flex;
                          flex-direction: column;
                          justify-content: space-between;
                      }
                  </style>
              </head>
              <body>
                  <div id="container">
                      <!-- 一個點居中 -->
                      <div class="item one">
                          <span class="item-one"></span>
                      </div>
                      <!-- 三點 -->
                      <div class="item two">
                          <span class="two1"></span>
                          <span class="two2"></span>
                          <span class="two3"></span>
                      </div>
                      <!-- 五點 -->
                      <div class="item three">
                          <div id="three_one">
                              <span></span>
                              <span></span>
                          </div>
                          <div id="three_two">
                              <span></span>
                          </div>
                          <div id="three_three">
                              <span></span>
                              <span></span>
                          </div>
                      </div>
                      <!-- 六點 -->
                      <div class="item four">
                          <div id="four1">
                              <span></span>
                              <span></span>
                              <span></span>
                          </div>
                          <div id="four2">
                              <span></span>
                              <span></span>
                              <span></span>
                          </div>
                      </div>
          
                  </div>
              </body>
          </html>

          測試結果

          在這里插入圖片描述



          遠程桌面訪問阿里云服務器的方法

          seo達人

          我用的是Windows版本的阿里云服務器。

          首先,打開服務器,找到已經創建好的服務器實例并點擊。

          之后會跳轉到實例頁面,點擊右側的“管理”

          然后配置安全組。安全組中就是設置哪些IP可以訪問我們的服務器。

          然后在安全組配置規則。

          添加新規則。

          想要讓Windows電腦遠程鏈接服務器需要開放3389端口。不然就無法用自己的電腦遠程鏈接服務器了。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          配置snmpd for windows

          seo達人

          1.安裝


          Windows 安裝請參考


          默認安裝為c:/usr/ ??梢詧绦腥缦挛募员銌觭nmp agent:


          snmpd

          會提示需要配置snmpd.conf。


          No log handling enabled - turning on stderr logging

          Warning: no access control information configured.

           It's unlikely this agent can serve any useful purpose in this state.

           Run "snmpconf -g basic_setup" to help you configure the snmpd.conf file for this agent.

          NET-SNMP version 5.5

          不必使用提示中的命令,因為此命令引用了perl,但是perl的對應模塊無法跑起來。看來perl要完蛋的傳說并不是空穴來風。


          snmpd.conf可以自己創建到/usr/etc/snmp/snmpd.conf內。


          2.修改配置文件


          配置之前的說明:我在網上看到的所有配置都是com2sec,group,access這三個配置,但是從默認的snmp.conf文件中有一段話:




          沒必要使用 com2sec/group/access配置,使用ro(w)user,ro(w)community結合合適的views,就可以覆蓋大多數需求了。


          2.1  配置監聽地址

          snmpd默認監聽本地IP的UDP161端口,等待snmp請求


          agentAddress udp:161

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務

          2.2  配置視圖(view)

          格式:view viewName type oid[mask]


          參數說明:  


             

             viewName : view名稱

             type : 有兩個值:include和exclude(包括或者排除)

             oid:可以訪問的oid(mib子樹)

             [mask]:對oid的掩碼 (可選參數)

          案例:


          view systemonly included .1.3.6.1.2.1

          2.3  配置共同體(community)

          格式:ro(w)community communityName source


          參數說明:


          rocommunity定義一個只讀權限的共同體,rwcommunity定義一個讀寫權限的共同體

          rocommunity6或   rwcommunity6表示監聽IPv6。

          communityName:共同體名稱

          source:可以訪問的IP地址范圍,默認為”default”,即任何IP都能訪問。


                         可選參數:-V viewName  限制共同體只能訪問viewName下的節點


          實際配置文件

          agentAddress udp:161

          view systemonly included .1.3.6.1.2.1

          rocommunity public default

          3. 測試

          修改完配置之后,重啟snmpd:


          snmpd

          連接測試:(該節點表示獲取主機內存大小)


          snmpwalk -v 1 -c public localhost .1.3.6.1.2.1.1.1

          SNMPv2-MIB::sysDescr.0 = STRING: Windows DESKTOP-70OA76Q 6.2.9200   Professional

          得到類似以上的結果,就說明snmpd配置成功。


          配置MIB庫

          Windows版本的snmp自帶有很多MIB,位于C:\usr\share\snmp\mibs


          因此可以對OID和MIB name做互相轉換:


          snmptranslate -On SNMPv2-MIB::sysDescr.0

          .1.3.6.1.2.1.1.1.0


          snmptranslate .1.3.6.1.2.1.1.1.0

          SNMPv2-MIB::sysDescr.0

          添加自己的mib

          之前的筆記有提到自己創建的一個定制mib文件


          https://github.com/1000copy/tbit-guard-snmp/blob/master/tbit.mib

          可以把它加入到C:\usr\share\snmp\mibs目錄內,文件名無所謂。然后為snmptranslate加入選項 -mALL ,表示加載所有在此目錄內的mib文件:


          snmptranslate -m ALL .1.3.6.1.4.1.66666

          你應該看到如下消息,表明mib加載成功:


          tbitinc::tbit

          詳細的說明如下:


          -m MIBLIST

               指定一個用冒號分隔的MIB模塊列表(不是文件),以便為這個應用程序加載。 這將覆蓋(或增加)環境變量MIBS、snmp.conf的內容。指令mibs,以及硬編碼到Net-SNMP庫中的MIBs列表。如果MIBLIST有一個前導的'-'或'+'字符,那么所列出的MIB模塊將被加載到默認列表之外,分別位于該列表之前或之后。

               否則,將加載指定的MIB,而不是這個默認列表。特殊關鍵字ALL用于加載MIB目錄搜索列表中的所有MIB模塊。 每一個名字不以". "開頭的文件都會被解析為是一個

               MIB文件。

          -M DIRLIST

               指定一個以冒號分隔的目錄列表來搜索MIB。 這將覆蓋(或增強)環境變量MIBDIRS和snmp.conf指令mibdirs。

               以及硬編碼到 Net-SNMP 庫中的默認目錄 (/usr/share/snmp/mibs)。


               如果DIRLIST有一個前導的'-'或'+'字符,那么給定的目錄會被添加到默認列表中,在這個列表的目錄之前或之后進行搜索。

               分別進行搜索。 否則,將搜索指定的目錄,而不是這個默認列表。


               請注意,列表中出現在后面的目錄要比前面的目錄優先。 要避免搜索任何MIB目錄,請將MIBDIRS環境中的

               變量為空字符串("")。


               請注意,使用-m選項或mibs配置指令指定的MIB將從-M選項(或等價物)列出的目錄中加載。

               mibfile指令采用的是指定MIB文件的完整路徑,所以不需要在MIB目錄搜索列表中出現。

          10分鐘徹底搞懂單頁面應用路由

          seo達人

          單頁面應用特征

          假設: 在一個 web 頁面中,有1個按鈕,點擊可跳轉到站內其他頁面。


          多頁面應用: 點擊按鈕,會從新加載一個html資源,刷新整個頁面;


          單頁面應用: 點擊按鈕,沒有新的html請求,只發生局部刷新,能營造出一種接近原生的體驗,如絲般順滑。


          SPA 單頁面應用為什么可以幾乎無刷新呢?因為它的SP——single-page。在第一次進入應用時,即返回了唯一的html頁面和它的公共靜態資源,后續的所謂“跳轉”,都不再從服務端拿html文件,只是DOM的替換操作,是模(jia)擬(zhuang)的。


          那么js又是怎么捕捉到組件切換的時機,并且無刷新變更瀏覽器url呢?靠hash和HTML5History。


          hash 路由

          特征

          類似www.xiaoming.html#bar 就是哈希路由,當 # 后面的哈希值發生變化時,不會向服務器請求數據,可以通過 hashchange 事件來監聽到 URL 的變化,從而進行DOM操作來模擬頁面跳轉

          不需要服務端配合

          對 SEO 不友好

          原理

          hash


          HTML5History 路由

          特征

          History 模式是 HTML5 新推出的功能,比之 hash 路由的方式直觀,長成類似這個樣子www.xiaoming.html/bar ,模擬頁面跳轉是通過 history.pushState(state, title, url) 來更新瀏覽器路由,路由變化時監聽 popstate 事件來操作DOM

          需要后端配合,進行重定向

          對 SEO 相對友好

          原理

          Html5 History


          vue-router 源碼解讀

          以 Vue 的路由vue-router為例,我們一起來擼一把它的源碼。


          Tips:因為,本篇的重點在于講解單頁面路由的兩種模式,所以,下面只列舉了一些關鍵代碼,主要講解:


          注冊插件

          VueRouter的構造函數,區分路由模式

          全局注冊組件

          hash / HTML5History模式的 push 和監聽方法

          transitionTo 方法

          注冊插件

          首先,作為一個插件,要有暴露一個install方法的自覺,給Vue爸爸去 use。


          源碼的install.js文件中,定義了注冊安裝插件的方法install,給每個組件的鉤子函數混入方法,并在beforeCreate鉤子執行時初始化路由:


          Vue.mixin({

           beforeCreate () {

             if (isDef(this.$options.router)) {

               this._routerRoot = this

               this._router = this.$options.router

               this._router.init(this)

               Vue.util.defineReactive(this, '_route', this._router.history.current)

             } else {

               this._routerRoot = (this.$parent && this.$parent._routerRoot) || this

             }

             registerInstance(this, this)

           },

           // 全文中以...來表示省略的方法

           ...

          });

          區分mode

          然后,我們從index.js找到整個插件的基類 VueRouter,不難看出,它是在constructor中,根據不同mode 采用不同路由實例的。


          ...

          import {install} from './install';

          import {HashHistory} from './history/hash';

          import {HTML5History} from './history/html5';

          ...

          export default class VueRouter {

           static install: () => void;

           constructor (options: RouterOptions = {}) {

             if (this.fallback) {

               mode = 'hash'

             }

             if (!inBrowser) {

               mode = 'abstract'

             }

             this.mode = mode

                   

             switch (mode) {

               case 'history':

                 this.history = new HTML5History(this, options.base)

                 break

               case 'hash':

                 this.history = new HashHistory(this, options.base, this.fallback)

                 break

              case 'abstract':

                 this.history = new AbstractHistory(this, options.base)

                 break

              default:

               if (process.env.NODE_ENV !== 'production') {

                 assert(false, `invalid mode: ${mode}`)

               }

             }

           }

          }

          全局注冊router-link組件

          這個時候,我們也許會問:使用 vue-router 時, 常見的<router-link/>、 <router-view/>又是在哪里引入的呢?


          回到install.js文件,它引入并全局注冊了 router-view、router-link組件:


          import View from './components/view';

          import Link from './components/link';

          ...

          Vue.component('RouterView', View);

          Vue.component('RouterLink', Link);

          在 ./components/link.js 中,<router-link/>組件上默認綁定了click事件,點擊觸發handler方法進行相應的路由操作。


          const handler = e => {

           if (guardEvent(e)) {

             if (this.replace) {

               router.replace(location, noop)

             } else {

               router.push(location, noop)

             }

          }

          };

          就像最開始提到的,VueRouter構造函數中對不同mode初始化了不同模式的 History 實例,因而router.replace、router.push的方式也不盡相同。接下來,我們分別扒拉下這兩個模式的源碼。


          hash模式

          history/hash.js 文件中,定義了HashHistory 類,這貨繼承自 history/base.js 的 History 基類。


          它的prototype上定義了push方法:在支持 HTML5History 模式的瀏覽器環境中(supportsPushState為 true),調用history.pushState來改變瀏覽器地址;其他瀏覽器環境中,則會直接用location.hash = path 來替換成新的 hash 地址。


          其實最開始讀到這里是有些疑問的,既然已經是 hash 模式為何還要判斷supportsPushState?是為了支持scrollBehavior,history.pushState可以傳參key過去,這樣每個url歷史都有一個key,用 key 保存了每個路由的位置信息。


          同時,原型上綁定的setupListeners 方法,負責監聽 hash 變更的時機:在支持 HTML5History 模式的瀏覽器環境中,監聽popstate事件;而其他瀏覽器中,則監聽hashchange。監聽到變化后,觸發handleRoutingEvent 方法,調用父類的transitionTo跳轉邏輯,進行 DOM 的替換操作。


          import { pushState, replaceState, supportsPushState } from '../util/push-state'

          ...

          export class HashHistory extends History {

           setupListeners () {

             ...

             const handleRoutingEvent = () => {

                 const current = this.current

                 if (!ensureSlash()) {

                   return

                 }

                 // transitionTo調用的父類History下的跳轉方法,跳轉后路徑會進行hash化

                 this.transitionTo(getHash(), route => {

                   if (supportsScroll) {

                     handleScroll(this.router, route, current, true)

                   }

                   if (!supportsPushState) {

                     replaceHash(route.fullPath)

                   }

                 })

               }

               const eventType = supportsPushState ? 'popstate' : 'hashchange'

               window.addEventListener(

                 eventType,

                 handleRoutingEvent

               )

               this.listeners.push(() => {

                 window.removeEventListener(eventType, handleRoutingEvent)

               })

           }

           

           push (location: RawLocation, onComplete?: Function, onAbort?: Function) {

             const { current: fromRoute } = this

             this.transitionTo(

               location,

               route => {

                 pushHash(route.fullPath)

                 handleScroll(this.router, route, fromRoute, false)

                 onComplete && onComplete(route)

               },

               onAbort

             )

           }

          }

          ...


          // 處理傳入path成hash形式的URL

          function getUrl (path) {

           const href = window.location.href

           const i = href.indexOf('#')

           const base = i >= 0 ? href.slice(0, i) : href

           return `${base}#${path}`

          }

          ...


          // 替換hash

          function pushHash (path) {

           if (supportsPushState) {

             pushState(getUrl(path))

           } else {

             window.location.hash = path

           }

          }


          // util/push-state.js文件中的方法

          export const supportsPushState =

           inBrowser &&

           (function () {

             const ua = window.navigator.userAgent


             if (

               (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&

               ua.indexOf('Mobile Safari') !== -1 &&

               ua.indexOf('Chrome') === -1 &&

               ua.indexOf('Windows Phone') === -1

             ) {

               return false

             }

             return window.history && typeof window.history.pushState === 'function'

           })()

          HTML5History模式

          類似的,HTML5History 類定義在 history/html5.js 中。


          定義push原型方法,調用history.pusheState修改瀏覽器的路徑。


          與此同時,原型setupListeners 方法對popstate進行了事件監聽,適時做 DOM 替換。


          import {pushState, replaceState, supportsPushState} from '../util/push-state';

          ...

          export class HTML5History extends History {


           setupListeners () {


             const handleRoutingEvent = () => {

             const current = this.current;

             const location = getLocation(this.base);

             if (this.current === START && location === this._startLocation) {

               return

             }


             this.transitionTo(location, route => {

               if (supportsScroll) {

                 handleScroll(router, route, current, true)

               }

             })

             }

             window.addEventListener('popstate', handleRoutingEvent)

             this.listeners.push(() => {

               window.removeEventListener('popstate', handleRoutingEvent)

             })

           }

           push (location: RawLocation, onComplete?: Function, onAbort?: Function) {

             const { current: fromRoute } = this

             this.transitionTo(location, route => {

               pushState(cleanPath(this.base + route.fullPath))

               handleScroll(this.router, route, fromRoute, false)

               onComplete && onComplete(route)

             }, onAbort)

           }

          }


          ...


          // util/push-state.js文件中的方法

          export function pushState (url?: string, replace?: boolean) {

           saveScrollPosition()

           const history = window.history

           try {

             if (replace) {

               const stateCopy = extend({}, history.state)

               stateCopy.key = getStateKey()

               history.replaceState(stateCopy, '', url)

             } else {

               history.pushState({ key: setStateKey(genStateKey()) }, '', url)

             }

           } catch (e) {

             window.location[replace ? 'replace' : 'assign'](url)

           }

          }

          transitionTo 處理路由變更邏輯

          上面提到的兩種路由模式,都在監聽時觸發了this.transitionTo,這到底是個啥呢?它其實是定義在 history/base.js 基類上的原型方法,用來處理路由的變更邏輯。

          先通過const route = this.router.match(location, this.current)對傳入的值與當前值進行對比,返回相應的路由對象;接著判斷新路由是否與當前路由相同,相同的話直接返回;不相同,則在this.confirmTransition中執行回調更新路由對象,并對視圖相關DOM進行替換操作。


          export class History {

          ...

          transitionTo (

             location: RawLocation,

             onComplete?: Function,

             onAbort?: Function

           ) {

             const route = this.router.match(location, this.current)

             this.confirmTransition(

               route,

               () => {

                 const prev = this.current

                 this.updateRoute(route)

                 onComplete && onComplete(route)

                 this.ensureURL()

                 this.router.afterHooks.forEach(hook => {

                   hook && hook(route, prev)

                 })


                 if (!this.ready) {

                   this.ready = true

                   this.readyCbs.forEach(cb => {

                     cb(route)

                   })

                 }

               },

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          Vue.js教程-目錄

          前端達人

          簡介

          • 本目錄作為Vue教程的首頁,所以會持續更新。
          • 如果某篇章節中有錯誤的地方,希望大家能夠指出來,我會更正,私信和評論里說都可以,不懂的地方也可以說,如果我也不會那就請教一下大佬們吧,畢竟我對前端的東西也不是特別了解,多多包涵!嘿嘿。

          章節列表

          章節名稱 地址
          Vue.js教程-安裝和HelloWorld https://coderhqf.blog.csdn.net/article/details/107574556
          Vue.js教程-Vue項目的目錄結構和.vue文件的構成 https://coderhqf.blog.csdn.net/article/details/107621070
          Vue.js教程-Vue基本指令 https://coderhqf.blog.csdn.net/article/details/107677588
          Vue.js教程-組件化開發 https://coderhqf.blog.csdn.net/article/details/107783664

          Vue簡介

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

          Vue特點

          • 易用:在有HTML CSS JavaScript的基礎上,快速上手。
          • 靈活:簡單小巧的核心,漸進式技術棧,足以應付任何規模的應用。
          • 性能:20kb min+gzip 運行大小、超快虛擬 DOM 、最省心的優化。

          Vue中數據觀測的實現

          • Vue.js利用了ES5的Object.defineProperty方法,直接將原生數據對象的屬性改造為getter和setter,在這兩個函數內部實現依賴的收集和觸發,而且完美支持嵌套的對象結構。對于數組,則通過包裹數組的可變方法(比如push)來監聽數組的變化。這使得操作Vue.js的數據和操作原生對象幾乎沒有差別[注:在添加/刪除屬性,或是修改數組特定位置元素時,需要調用特定的函數,如obj.$add(key, value)才能觸發更新。這是受ES5的語言特性所限。],數據操作的邏輯更為清晰流暢,和第三方數據同步方案的整合也更為方便。

          Vue項目打包

          • 在構建大型應用時,推薦使用Webpack+vue-loader這個組合以使針對組件的開發更。
          • 在后面的章節中會細說怎么打包并部署到服務器上,也會講怎么白嫖阿里云(學生版),好像有大佬寫過這個文章,大家搜一下也行,最重要的還是開發。

          Vue的組件化開發

          • Vue最主要的是組件化開發,因為是單頁面,也就是在一個頁面中渲染出多個頁面的效果,這個特點能夠讓非常多的組件在不同的項目中重復使用。
          • Vue中的組件基于Web components進行了上層功能的實現,例如數據綁定、動畫系統等。

          Vue與后端的數據交互:axios

          • 傳統的一般都用Ajax,但如果請求有先后關系的話就容易產生回調地獄,套娃套的吧。
          • Vue2之后就推薦使用axios了,等寫到前后端交互的時候再講這個就行。

          相關說明

          • Vue參考了AngularJS、KnockoutJS、Ractive.js、Rivets.js,可以是把他們的缺點都優化成了自己的優點,參考過程中不但去其糟粕,還加入了自己的特性,但目前也只有國內用Vue的比較多,畢竟社區小,資源少,但以后應該會是潮流,因為開發快好上手。
          • 其實Vue相對來說是非常好上手的,因為它只專注于視圖層。如果只是要用的話,其實對原理也不用太糾結,但既然要全棧,何不貫徹到底,我也是在學習中,如果想正規學習的話,我比較推薦coderwhy-王紅君老師的課,他講的挺好的,有些原理講的也是很清楚的,渠道嘛,B站大學、騰訊課堂啥的都有,還有他的微博,大家可以去網上找。
          • 再強調一遍,如果發現不對的地方請聯系我,因為不想誤人子弟,畢竟這是自己的總結,也不想以后自己還用著錯誤的東西,嘿嘿嘿。。。
          • Vue作為現在國內最潮流的前端框架,也逐漸成為后端開發人員需要學的新知識了,我看現在很多后端崗位的面試里都會提到這個前端框架,所以大家學一下是不虧的。
          • 在CSDN雜志中有一篇文章:Vue.js:輕量的前端組件化方案,如果大家有興趣就看看吧。

          版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
          本文鏈接:https://blog.csdn.net/weixin_45062103/article/details/107763788

          JavaScript中的for循環

          seo達人

          JavaScript 語言中的 for 循環用于多次執行代碼塊,它是 JavaScript 中最常用的一個循環工具,還可用于數組的遍歷循環等。


          我們為什么要使用 for 循環呢?打個比方,例如我們想要控制臺輸出1到1000之間的所有數字,如果單寫輸出語句,要寫1000句代碼,但是如果使用 for 循環,幾句代碼就能實現??傊?,使用 for 循環能夠讓我們寫代碼更方便快捷(當然啦,否則要它干嘛)。


          for 循環語法

          語法如下所示:


          for(變量初始化; 條件表達式; 變量更新) {

             // 條件表達式為true時執行的語句塊

          }

          變量初始化,表示代碼塊開始前執行。

          條件表達式,定義運行循環代碼塊的條件。

          變量更新,在循環代碼塊每次被執行之后再執行。

          示例:

          例如我們在一個HTML文件中,編寫如下代碼,實現計算1到100的總和:


          <!DOCTYPE html>

          <html>

          <head>

          <meta charset="utf-8">

          <title>JS_俠課島(9xkd.com)</title>

          </head>

          <body>

          <script>

           var result = 0;

           for(var i = 1; i <= 100; i++) {

             result = result + i;

           }

           alert(result);

          </script>

          </body>  

          </html>

          在瀏覽器中打開這個文件,會彈出一個彈出層,彈出層中顯示的是1到100的總和:



          上述代碼中,我們聲明了一個變量 result 并給它賦值為 0,表示初始的總和為 0 。


          然后在 for 循環中三個語句:


          變量初始化 i = 1,表示從 1 開始計算。

          條件表達式 i <= 100,表示只要 i 小于等于 100 循環就會一直執行,當 i 大于 100 循環會停止。

          變量更新 i++,之前我們學運算符的時候學過,這是遞增運算符 ++,表示為其操作數增加 1。

          此時我們可以一點點來看這個 for 循環:


          第一次循環: result = 0 + 1   // 此時result值為0,  i的值為1

          第二次循環: result = 1 + 2   // 此時result值為0+1,i的值為2

          第三次循環: result = 3 + 3   // 此時result值為1+2,i的值為3

          第四次循環: result = 6 + 4   // 此時result值為3+3,i的值為4

          第五次循環: result = 10 + 5  // 此時result值為6+4,i的值為5

          ...

          我們只需要搞清楚 for 循環中的執行原理,不需要手動來計算求和,只要寫好代碼,執行代碼后計算機會很快會告訴我們1到 100 的總和。


          再補充一下,上述代碼中result = result + i,我們也可以寫成 result += i,這是我們之前學過的加賦值運算符,還記得嗎?


          示例:

          再來看一個例子,例如我們可以使用 for 循環來實現數組遍歷,首先定義一個數組 lst:


          var lst = ["a", "b", "c", "d", "e"];

          在寫 for 循環時,首先就是要搞清楚小括號里面的三個語句,因為我們可以通過數組中元素的下標索引來獲取元素的值,而數組的索引又是從 0 開始,所以變量初始化可以設置為i = 0。第二個條件表達式,因為數組中最后一個索引為 lst.length - 1,所以只要小于等于 lst.length - 1,循環就會一直執行。而i <= lst.length - 1 就相當于 i<lst.length。第三個變量更新,當循環每循環一次,索引值就加一,所以為 i++。


          所以循環可以像下面這樣寫:


          for(i = 0; i<lst.length; i++){

             console.log(lst[i]);  // 輸出數組中的元素值,從索引為0的值開始輸出,每次加1,一直到lst.length-1

          }

          輸出:


          a

          b

          c

          d

          e

          其實遍歷數組還有一種更好的方法,就是使用 for...in 循環語句來遍歷數組。


          for...in 循環

          for...in 循環主要用于遍歷數組或對象屬性,對數組或對象的屬性進行循環操作。for...in 循環中的代碼每執行一次,就會對數組的元素或者對象的屬性進行一次操作。


          語法如下:


          for (變量 in 對象) {

             // 代碼塊

          }

          for 循環括號內的變量是用來指定變量,指定的可以是數組對象或者是對象屬性。


          示例:

          使用 for...in 循環遍歷我們定義好的 lst 數組:


          var lst = ["a", "b", "c", "d", "e"];

          for(var l in lst){

             console.log(lst[l]);

          }

          輸出:


          a

          b

          c

          d

          e

          除了數組,for...in 循環還可以遍歷對象,例如我們遍歷 俠俠 的個人基本信息:


          var object = {

             姓名:'俠俠',

             年齡:'22',

             性別:'男',

             出生日期:'1997-08-05',

             職業:'程序員',

             特長:'跳舞'

          }


          for(var i in object) {

             console.log(i + ":" + object[i]);

          }

          輸出:


          姓名: 俠俠

          年齡: 22

          性別: 男

          出生日期: 1997-08-05

          職業:程序員

          特長:跳舞

          動手小練習

          請自定義一個長度為7的數組,然后通過 for 循環將數組中的元素遍歷出來。

          求和:1~100的奇數和。

          求和:1~100的偶數和。

          使用對象定義一個人的個人信息(包括姓名、性別、年齡、出生日期、興趣愛好、職業、特長等),然后使用 for...in 循環將這些信息遍歷輸出。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          封裝element-ui表格

          seo達人

          表格需求

          一般管理系統對表格會有以下需求


          可以分頁(需要有分頁條)

          可以多選(表格帶復選框)

          頂部需要加一些操作按鈕(新增,刪除等等)

          表格每行行尾有操作按鈕

          表格行可以編輯

          如下圖為一個示例表格




          如果我們直接使用element-ui提供的組件的話,那么開發一個這樣的表格就需要使用到以下內容


          需要使用表格的插槽功能,開發每一行的按鈕

          需要通過樣式調整頂部按鈕,表格,分頁條的布局樣式

          需要監聽分頁的事件然后去刷新表格數據

          頂部按鈕或操作按鈕如果需要獲取表格數據,需要調用表格提供的api

          對于有行編輯的需求,還需要通過插槽去渲染行編輯的內容,同時要控制行編輯的開關

          不僅僅開發表格比較麻煩,而且還要考慮團隊協作,如果每個人實現表格的方式存在差別,那么可能后期的維護成本也會變得很高。那怎么辦呢?


          項目安裝

          安裝插件

          在使用element-ui的項目中,可以通過以下命令進行安裝


          npm install vue-elementui-table -S

          在項目中使用

          在main.js中添加以下代碼


          import ZjTable from 'vue-element-table'


          Vue.use(ZjTable)

          然后即可像下文中的使用方式進行使用


          表格配置

          為了滿足團隊快速開發的需要,小編對上面提出來的需求進行了封裝,然后使用的時候,開發人員只需要配置一些JSON便可以完成以上功能的開發。


          基礎配置

          一個基礎的表格包含了數據和列信息,那么如何用封裝的表格去配置呢?


          <template>

           <zj-table

             :columns="columns"

             :data="data"

             :pagination="false"

           />

          </template>

          <script>

          export default {

           data() {

             return {

               // 表格的列信息, 數組每一項代表一個字段,可以使用element 列屬性的所有屬性,以下僅為示例

               columns: Object.freeze([

                 {

                   // 表頭顯示的文字

                   label: '姓名',

                   // 對應數據里面的字段

                   prop: 'name'

                 },

                 {

                   label: '性別',

                   prop: 'sex',

                   // 格式化表格,與element-ui 的表格屬性相同

                   formatter(row, column, cellValue) {

                     return cellValue === 1 ? '男' : '女'

                   }

                 },

                 {

                   label: '年齡',

                   prop: 'age'

                 }

               ]),

               data: [

                 {

                   name: '子君',

                   sex: 1,

                   age: 18

                 }

               ]

             }

           }

          }

          </script>

          通過上面的配置,就可以完成一個基礎表格的開發,完整代碼見 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/base.vue,效果如下圖所示




          表格默認會顯示復選框,也可以通過配置selectable屬性來關閉掉


          添加分頁

          簡單的表格用封裝之后的或未封裝的開發工作量區別并不大,我們繼續為表格添加上分頁


          <template>

             <!--

             current-page.sync 表示頁碼, 添加上 .sync 在頁碼發生變化時自動同步頁碼

             page-size.sync 每頁條數

             total  總條數

             height="auto" 配置height:auto, 表格高度會根據內容自動調整,如果不指定,表格將保持充滿父容器,同時表頭會固定,不跟隨滾動條滾動

             @page-change 無論pageSize currentPage 哪一個變化,都會觸發這個事件

           -->

           <zj-table

             v-loading="loading"

             :columns="columns"

             :data="data"

             :current-page.sync="currentPage"

             :page-size.sync="pageSize"

             :total="total"

             height="auto"

             @page-change="$_handlePageChange"

           />

          </template>

          <script>

          export default {

           data() {

             return {

               columns: Object.freeze([

                 // 列字段與上例一樣,此處省略

               ]),

               data: [],

               // 當前頁碼

               currentPage: 1,

               // 每頁條數

               pageSize: 10,

               // 總條數

               total: 0,

               // 是否顯示loading

               loading: false

             }

           },

           created() {

             this.loadData()

           },

           methods: {

             // 加載表格數據

             loadData() {

               this.loading = true

               setTimeout(() => {

                 // 假設總條數是40條

                 this.total = 40

                 const { currentPage, pageSize } = this

                 // 模擬數據請求獲取數據

                 this.data = new Array(pageSize).fill({}).map((item, index) => {

                   return {

                     name: `子君${currentPage + (index + 1) * 10}`,

                     sex: Math.random() > 0.5 ? 1 : 0,

                     age: Math.floor(Math.random() * 100)

                   }

                 })

                 this.loading = false

               }, 1000)

             },

             $_handlePageChange() {

               // 因為上面設置屬性指定了.sync,所以這兩個屬性會自動變化

               console.log(this.pageSize, this.currentPage)

               // 分頁發生變化,重新請求數據

               this.loadData()

             }

           }

          }

          </script>

          完整代碼請參考 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/pagination.vue


          通過封裝,表格將自帶分頁功能,通過上面代碼,實現效果如下所示,是不是變得簡單了一些。接下來我們繼續給表格添加按鈕




          添加頂部按鈕

          表格上面可能會有新增,刪除等等按鈕,怎么辦呢,接下來我們繼續通過配置去添加按鈕


          <template>

           <zj-table

             :buttons="buttons"

           />

          </template>

          <script>

          export default {

           data() {

             return {

               buttons: Object.freeze([

                 {

                   // id 必須有而且是在當前按鈕數組里面是唯一的

                   id: 'add',

                   text: '新增',

                   type: 'primary',

                   icon: 'el-icon-circle-plus',

                   click: this.$_handleAdd

                 },

                 {

                   id: 'delete',

                   text: '刪除',

                   // rows 是表格選中的行,如果沒有選中行,則禁用刪除按鈕, disabled可以是一個boolean值或者函數

                   disabled: rows => !rows.length,

                   click: this.$_handleRemove

                 },

                 {

                   id: 'auth',

                   text: '這個按鈕根據權限顯示',

                   // 可以通過返回 true/false來控制按鈕是否顯示

                   before: (/** rows */) => {

                     return true

                   }

                 },

                 // 可以配置下拉按鈕哦

                 {

                   id: 'dropdown',

                   text: '下拉按鈕',

                   children: [

                     {

                       id: 'moveUp',

                       text: '上移',

                       icon: 'el-icon-arrow-up',

                       click: () => {

                         console.log('上移')

                       }

                     },

                     {

                       id: 'moveDown',

                       text: '下移',

                       icon: 'el-icon-arrow-down',

                       disabled: rows => !rows.length,

                       click: () => {

                         console.log('下移')

                       }

                     }

                   ]

                 }

               ])

             }

           },

           created() {},

           methods: {

             // 新增

             $_handleAdd() {

               this.$alert('點擊了新增按鈕')

             },

             // 頂部按鈕會自動將表格所選的行傳出來

             $_handleRemove(rows) {

               const ids = rows.map(({ id }) => id)

               this.$alert(`要刪除的行id為${ids.join(',')}`)

             },

             // 關注作者公眾號

             $_handleFollowAuthor() {}

           }

          }

          </script>

          表格頂部可以添加普通的按鈕,也可以添加下拉按鈕,同時還可以通過before來配置按鈕是否顯示,disabled來配置按鈕是否禁用,上面完整代碼見 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


          通過上面的代碼就可以配置出下面的表格,是不是很簡單呢?




          表格頂部可以有按鈕,行尾也是可以添加按鈕的,一起來看看


          行操作按鈕

          一般我們會將一些單行操作的按鈕放在行尾,比如編輯,下載等按鈕,那如何給行尾配置按鈕呢?


          <template>

           <zj-table

             :columns="columns"

           />

          </template>

          <script>

          export default {

           data() {

             return {

               columns: Object.freeze([

                 {

                   // 可以指定列的寬度,與element-ui原生用法一致

                   width: 220,

                   label: '姓名',

                   prop: 'name'

                 },

                 // 行編輯按鈕,在表格末尾出現,自動鎖定右側

                 {

                   width: 180,

                   label: '操作',

                   // 通過 actions 指定行尾按鈕

                   actions: [

                     {

                       id: 'follow',

                       text: '關注作者',

                       click: this.$_handleFollowAuthor

                     },

                     {

                       id: 'edit',

                       text: '編輯',

                       // 可以通過before控制按鈕是否顯示,比如下面年齡四十歲的才會顯示編輯按鈕

                       before(row) {

                         return row.age < 40

                       },

                       click: this.$_handleEdit

                     },

                     {

                       id: 'delete',

                       text: '刪除',

                       icon: 'el-icon-delete',

                       disabled(row) {

                         return row.sex === 0

                       },

                       // 為了拿到this,這里需要用箭頭函數

                       click: () => {

                         this.$alert('女生被禁止刪除了')

                       }

                     }

                   ]

                 }

               ])

             }

           },

           methods: {

             // 關注作者公眾號

             $_handleFollowAuthor() {

                     console.log('微信搜索【前端有的玩】,這是對小編最大的支持')

             },

             /**

              * row 這一行的數據

              */

             $_handleEdit(row, column) {

               this.$alert(`點擊了姓名為【${row.name}】的行上的按鈕`)

             }

           }

          }

          </script>

          行操作按鈕會被凍結到表格最右側,不會跟隨滾動條滾動而滾動,上面完整代碼見, https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


          通過上面的代碼就可以完成以下效果




          最后再來一起看看行編輯


          行編輯

          比如上例,我希望點擊行尾的編輯按鈕的時候,可以直接在行上面編輯用戶的姓名與性別,如何配置呢?


          <template>

           <zj-table

             ref="table"

             :columns="columns"

             :data="data"

           />

          </template>

          <script>

          export default {

           data() {

             return {

               columns: Object.freeze([

                 {

                   label: '姓名',

                   prop: 'name',

                   editable: true,

                   field: {

                     componentType: 'input',

                     rules: [

                       {

                         required: true,

                         message: '請輸入姓名'

                       }

                     ]

                   }

                 },

                 {

                   label: '性別',

                   prop: 'sex',

                   // 格式化表格,與element-ui 的表格屬性相同

                   formatter(row, column, cellValue) {

                     return cellValue === '1' ? '男' : '女'

                   },

                   editable: true,

                   field: {

                     componentType: 'select',

                     options: [

                       {

                         label: '男',

                         value: '1'

                       },

                       {

                         label: '女',

                         value: '0'

                       }

                     ]

                   }

                 },

                 {

                   label: '年齡',

                   prop: 'age',

                   editable: true,

                   field: {

                     componentType: 'number'

                   }

                 },

                 {

                   label: '操作',

                   actions: [

                     {

                       id: 'edit',

                       text: '編輯',

                       // 如果當前行啟用了編輯,則不顯示編輯按鈕

                       before: row => {

                         return !this.editIds.includes(row.id)

                       },

                       click: this.$_handleEdit

                     },

                     {

                       id: 'save',

                       text: '保存',

                       // 如果當前行啟用了編輯,則顯示保存按鈕

                       before: row => {

                         return this.editIds.includes(row.id)

                       },

                       click: this.$_handleSave

                     }

                   ]

                 }

               ]),

               data: [

                 {

                   // 行編輯必須指定rowKey字段,默認是id,如果修改為其他字段,需要給表格指定row-key="字段名"

                   id: '0',

                   name: '子君',

                   sex: '1',

                   age: 18

                 },

                 {

                   // 行編輯必須指定rowKey字段,默認是id,如果修改為其他字段,需要給表格指定row-key="字段名"

                   id: '1',

                   name: '子君1',

                   sex: '0',

                   age: 18

                 }

               ],

               editIds: []

             }

           },

           methods: {

             $_handleEdit(row) {

               // 通過調用 startEditRow 可以開啟行編輯

               this.$refs.table.startEditRow(row.id)

               // 記錄開啟了行編輯的id

               this.editIds.push(row.id)

             },

             $_handleSave(row) {

               // 點擊保存的時候,通過endEditRow 結束行編輯

               this.$refs.table.endEditRow(row.id, (valid, result, oldRow) => {

                 // 如果有表單驗證,則valid會返回是否驗證成功

                 if (valid) {

                   console.log('修改之后的數據', result)

                   console.log('原始數據', oldRow)

                   const index = this.editIds.findIndex(item => item === row.id)

                   this.editIds.splice(index, 1)

                 } else {

                   // 如果校驗失敗,則返回校驗的第一個輸入框的異常信息

                   console.log(result)

                   this.$message.error(result.message)

                 }

               })

             }

           }

          }

          </script>

          不需要使用插槽就可以完成行編輯,是不是很開心。上述完整代碼見 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/row-edit.vue


          效果如下圖所示:




          其他功能

          除了上面的功能之外,表格還可以配置其他許多功能,比如


          可以指定字段為鏈接列,需要給列配置link屬性

          可以通過插槽自定義頂部按鈕,行操作按鈕,行字段等

          可以在按鈕區域右側通過插槽配置其他內容

          其他等等

          表格開發說明

          通過上面的代碼示例,我們已經知道了封裝之后的表格可以完成哪些事情,接下來一起來看看表格是如何實現的。完整代碼見 https://github.com/snowzijun/vue-element-table/tree/master/src/components/zj-table


          表格布局

          整個表格是通過JSX來封裝的,因為JSX使用起來更加靈活。對于我們封裝的表格,我們從豎向可以分為三部分,分別是頂部按鈕區,中間表格區,底部分頁區,如何去實現三個區域的布局呢,核心代碼如下


          render(h) {

             // 按鈕區域

             const toolbar = this.$_renderToolbar(h)

             // 表格區域

             const table = this.$_renderTable(h)

             // 分頁區域

             const page = this.$_renderPage(h)


             return (

               <div class="zj-table" style={{ height: this.tableContainerHeight }}>

                 {toolbar}

                 {table}

                 {page}

               </div>

             )

           }

          通過三個render函數分別渲染對應區域,然后將三個區域組合在一起。


          渲染表格列

          通過前文的講解,我們可以將表格的列分為以下幾種


          常規列

          行編輯列

          操作按鈕列

          插槽列

          鏈接列(文檔后續完善)

          嵌套列(文檔后續完善)

             $_renderColumns(h, columns) {

               // 整體是否排序

               let sortable = this.sortable ? 'custom' : false

               return columns

                 .filter(column => {

                   const { hidden } = column

                   if (hidden !== undefined) {

                     if (typeof hidden === 'function') {

                       return hidden({

                         columns,

                         column

                       })

                     }

                     return hidden

                   }

                   return true

                 })

                 .map(column => {

                   const {

                     useSlot = false,

                     // 如果存在操作按鈕,則actions為非空數組

                     actions = [],

                     // 是否可編輯列, 對于可編輯列需要動態啟用編輯

                     editable = false,

                     // 是否有嵌套列

                     nests,

                     // 是否可點擊

                     link = false

                   } = column

                   let newSortable = sortable

                   if (column.sortable !== undefined) {

                     newSortable = column.sortable ? 'custom' : false

                   }

                   column = {

                     ...column,

                     sortable: newSortable

                   }

                   if (nests && nests.length) {

                     // 使用嵌套列

                     return this.$_renderNestColumn(h, column)

                   } else if (editable) {

                     // 使用編輯列

                     return this.$_renderEditColumn(h, column)

                   } else if (useSlot) {

                     // 使用插槽列

                     return this.$_renderSlotColumn(h, column)

                   } else if (actions && actions.length > 0) {

                     // 使用操作列

                     column.sortable = false

                     return this.$_renderActionColumn(h, column)

                   } else if (link) {

                     // 使用鏈接列

                     return this.$_renderLinkColumn(h, column)

                   } else {

                     // 使用默認列

                     return this.$_renderDefaultColumn(h, column)

                   }

                 })

             },

          行編輯列

          當前表格行編輯支持input,select,datepicker,TimeSelect,InputNumber等組件,具體渲染代碼如下所示


          // 編輯單元格

             $_renderEditCell(h, field) {

               const components = {

                 input: Input,

                 select: ZjSelect,

                 date: DatePicker,

                 time: TimeSelect,

                 number: InputNumber

               }

               const componentType = field.componentType

               const component = components[componentType]

               if (component) {

                 return this.$_renderField(h, field, component)

               } else if (componentType === 'custom') {

                 // 如果自定義,可以通過component指定組件

                 return this.$_renderField(h, field, field.component)

               }

               return this.$_renderField(h, field, Input)

             },

             $_renderField(h, field, Component) {

               // 編輯行的id字段

               const { rowId, events = {}, nativeEvents = {} } = field


               const getEvents = events => {

                 const newEvents = {}

                 Object.keys(events).forEach(key => {

                   const event = events[key]

                   newEvents[key] = (...rest) => {

                     const args = [

                       ...rest,

                       {

                         rowId,

                         row: this.editRowsData[rowId],

                         value: this.editRowsData[rowId][field.prop]

                       }

                     ]

                     return event(...args)

                   }

                 })

                 return newEvents

               }

               // 事件改寫

               const newEvents = getEvents(events)

               const newNativeEvents = getEvents(nativeEvents)

               return (

                 <Component

                   size="small"

                   on={newEvents}

                   nativeOn={newNativeEvents}

                   v-model={this.editRowsData[rowId][field.prop]}

                   {...{

                     attrs: field,

                     props: field

                   }}

                 />

               )

             }

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務


          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合