92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

首頁

函數節流與函數防抖的區別

seo達人

函數節流與函數防抖是我們解決頻繁觸發DOM事件的兩種常用解決方案,但是經常傻傻分不清楚。。。這不,在項目中又用遇到了,在此處記錄一下



函數防抖 debounce

原理:將若干函數調用合成為一次,并在給定時間過去之后,或者連續事件完全觸發完成之后,調用一次(僅僅只會調用一次?。。。。。。。。?!)。



舉個栗子:滾動scroll事件,不?;瑒訚L輪會連續觸發多次滾動事件,從而調用綁定的回調函數,我們希望當我們停止滾動的時,才觸發一次回調,這時可以使用函數防抖。



原理性代碼及測試:



// 給盒子較大的height,容易看到效果

<style>

    * {

        padding: 0;

        margin: 0;

    }



    .box {

        width: 800px;

        height: 1200px;

    }

</style>

<body>

    <div class="container">

        <div class="box" style="background: tomato"></div>

        <div class="box" style="background: skyblue"></div>

        <div class="box" style="background: red"></div>

        <div class="box" style="background: yellow"></div>

    </div>

    <script>

        window.onload = function() {

            const decounce = function(fn, delay) {

                let timer = null



                return function() {

                    const context = this

                    let args = arguments

                    clearTimeout(timer) // 每次調用debounce函數都會將前一次的timer清空,確保只執行一次

                    timer = setTimeout(() => {

                        fn.apply(context, args)

                    }, delay)

                }

            }



            let num = 0



            function scrollTap() {

                num++

                console.log(看看num吧 ${num})

            }

            // 此處的觸發時間間隔設置的很小

            document.addEventListener('scroll', decounce(scrollTap, 500))

            // document.addEventListener('scroll', scrollTap)

        }

    </script>

</body>



此處的觸發時間間隔設置的很小,如果勻速不間斷的滾動,不斷觸發scroll事件,如果不用debounce處理,可以發現num改變了很多次,用了debounce函數防抖,num在一次上時間的滾動中只改變了一次。



調用debouce使scrollTap防抖之后的結果:



直接調用scrollTap的結果:





補充:瀏覽器在處理setTimeout和setInterval時,有最小時間間隔。

setTimeout的最短時間間隔是4毫秒;

setInterval的最短間隔時間是10毫秒,也就是說,小于10毫秒的時間間隔會被調整到10毫秒。

事實上,未優化時,scroll事件頻繁觸發的時間間隔也是這個最小時間間隔。

也就是說,當我們在debounce函數中的間隔事件設置不恰當(小于這個最小時間間隔),會使debounce無效。



函數節流 throttle

原理:當達到了一定的時間間隔就會執行一次;可以理解為是縮減執行頻率



舉個栗子:還是以scroll滾動事件來說吧,滾動事件是及其消耗瀏覽器性能的,不停觸發。以我在項目中碰到的問題,移動端通過scroll實現分頁,不斷滾動,我們不希望不斷發送請求,只有當達到某個條件,比如,距離手機窗口底部150px才發送一個請求,接下來就是展示新頁面的請求,不停滾動,如此反復;這個時候就得用到函數節流。



原理性代碼及實現



// 函數節流 throttle

// 方法一:定時器實現

const throttle = function(fn,delay) {

  let timer = null



  return function() {

    const context = this

    let args = arguments

    if(!timer) {

      timer = setTimeout(() => {

        fn.apply(context,args) 

        clearTimeout(timer) 

      },delay)

    }

  }

}



// 方法二:時間戳

const throttle2 = function(fn, delay) {

  let preTime = Date.now()



  return function() {

      const context = this

      let args = arguments

      let doTime = Date.now()

      if (doTime - preTime >= delay) {

          fn.apply(context, args)

          preTime = Date.now()

      }

  }

}



需要注意的是定時器方法實現throttle方法和debounce方法的不同:



在debounce中:在執行setTimeout函數之前總會將timer用setTimeout清除,取消延遲代碼塊,確保只執行一次

在throttle中:只要timer存在就會執行setTimeout,在setTimeout內部每次清空這個timer,但是延遲代碼塊已經執行啦,確保一定頻率執行一次




我們依舊可以在html頁面中進行測試scroll事件,html和css代碼同debounce,此處不贅述,運行結果是(可以說是一場漫長的滾輪滾動了):





最后再來瞅瞅項目中封裝好的debounce和throttle函數,可以說是很優秀了,考慮的特別全面,希望自己以后封裝的函數也能考慮的這么全面吧,加油!



/*

 
空閑控制 返回函數連續調用時,空閑時間必須大于或等于 wait,func 才會執行

 

 
@param  {function} func        傳入函數,最后一個參數是額外增加的this對象,.apply(this, args) 這種方式,this無法傳遞進函數

  @param  {number}   wait        表示時間窗口的間隔

 
@param  {boolean}  immediate   設置為ture時,調用觸發于開始邊界而不是結束邊界

  @return {function}             返回客戶調用函數

 
/

const debounce = function(func, wait, immediate) {

    let timeout, args, context, timestamp, result;



    const later = function() {

        // 據上一次觸發時間間隔

        let last = Number(new Date()) - timestamp;



        // 上次被包裝函數被調用時間間隔last小于設定時間間隔wait

        if (last < wait && last > 0) {

            timeout = setTimeout(later, wait - last);

        } else {

            timeout = null;

            // 如果設定為immediate===true,因為開始邊界已經調用過了此處無需調用

            if (!immediate) {

                result = func.call(context, ...args, context);

                if (!timeout) {

                    context = args = null;

                }

            }

        }

    };



    return function(..._args) {

        context = this;

        args = _args;

        timestamp = Number(new Date());

        const callNow = immediate && !timeout;

        // 如果延時不存在,重新設定延時

        if (!timeout) {

            timeout = setTimeout(later, wait);

        }

        if (callNow) {

            result = func.call(context, ...args, context);

            context = args = null;

        }



        return result;

    };

};



/*

 
頻率控制 返回函數連續調用時,func 執行頻率限定為 次 / wait

 

 
@param  {function}   func      傳入函數

  @param  {number}     wait      表示時間窗口的間隔

 
@param  {object}     options   如果想忽略開始邊界上的調用,傳入{leading: false}。

                                 如果想忽略結尾邊界上的調用,傳入{trailing: false}

 
@return {function}             返回客戶調用函數

 */

const throttle = function(func, wait, options) {

    let context, args, result;

    let timeout = null;

    // 上次執行時間點

    let previous = 0;

    if (!options) options = {};

    // 延遲執行函數

    let later = function() {

        // 若設定了開始邊界不執行選項,上次執行時間始終為0

        previous = options.leading === false ? 0 : Number(new Date());

        timeout = null;

        result = func.apply(context, args);

        if (!timeout) context = args = null;

    };

    return function(..._args) {

        let now = Number(new Date());

        // 首次執行時,如果設定了開始邊界不執行選項,將上次執行時間設定為當前時間。

        if (!previous && options.leading === false) previous = now;

        // 延遲執行時間間隔

        let remaining = wait - (now - previous);

        context = this;

        args = _args;

        // 延遲時間間隔remaining小于等于0,表示上次執行至此所間隔時間已經超過一個時間窗口

        // remaining大于時間窗口wait,表示客戶端系統時間被調整過

        if (remaining <= 0 || remaining > wait) {

            clearTimeout(timeout);

            timeout = null;

            previous = now;

            result = func.apply(context, args);

            if (!timeout) context = args = null;

            //如果延遲執行不存在,且沒有設定結尾邊界不執行選項

        } else if (!timeout && options.trailing !== false) {

            timeout = setTimeout(later, remaining);

        }

        return result;

    };

};


超簡單入門Vuex小示例

seo達人

寫在前面

本文旨在通過一個簡單的例子,練習vuex的幾個常用方法,使初學者以最快的速度跑起來一個vue + vuex的示例。

學習vuex需要你知道vue的一些基礎知識和用法。相信點開本文的同學都具備這個基礎。

另外對vuex已經比較熟悉的大佬可以忽略本文。

生成基于vue的項目

基于vue-cli腳手架生成一個vue項目
常用npm命令:

npm i vue-vli -g vue --version vue init webpack 項目名 

進入項目目錄,使用npm run dev先試著跑一下。

一般不會出現問題,試跑成功后,就可以寫我們的vuex程序了。

使用vue完成的示例

使用vuex首先得安裝vuex,命令:

npm i vuex --save

介紹一下我們的超簡單Demo,一個父組件,一個子組件,父組件有一個數據,子組件有一個數據,想要將這兩個數據都放置到vuex的state中,然后父組件可以修改自己的和子組件的數據。子組件可以修改父組件和自己的數據。

先放效果圖,初始化效果如下:

如果想通過父組件觸發子組件的數據,就點“改變子組件文本”按鈕,點擊后效果如下:

如果想通過子組件修改父組件的數據,就在子組件點擊“修改父組件文本”按鈕,點擊后效果如下:

代碼文件介紹

首先是Parent.vue組件

<template> <div class="parent"> <h3>這里是父組件</h3> <button type="button" @click="clickHandler">修改自己文本</button> <button type="button" @click="clickHandler2">修改子組件文本</button> <div>Test: {{msg}}</div> <child></child> </div> </template> <script> import store from '../vuex' import Child from './Child.vue' export default { computed: {
            msg(){ return store.state.testMsg;
            }
        }, methods:{
            clickHandler(){
                store.commit('changeTestMsg', '父組件修改自己后的文本')
            },
            clickHandler2(){
                store.commit('changeChildText', '父組件修改子組件后的文本')
            }
        }, components:{ 'child': Child
        },
        store,
    } </script> <style scoped> .parent{ background-color: #00BBFF; height: 400px;
    } </style> 

下面是Child.vue子組件

<template> <div class="child"> <h3>這里是子組件</h3> <div>childText: {{msg}}</div> <button type="button" @click="clickHandler">修改父組件文本</button> <button type="button" @click="clickHandler2">修改自己文本</button> </div> </template> <script> import store from '../vuex' export default { name: "Child", computed:{
            msg(){ return store.state.childText;
            }
        }, methods: {
            clickHandler(){
                store.commit("changeTestMsg", "子組件修改父組件后的文本");
            },
            clickHandler2(){
                store.commit("changeChildText", "子組件修改自己后的文本");
            }
        },
        store
    } </script> <style scoped> .child{ background-color: palegreen; border:1px solid black; height:200px; margin:10px;
    } </style> 

最后是vuex的配置文件

 import Vue from 'vue' import Vuex from 'vuex';

Vue.use(Vuex) const state = { testMsg: '原始文本', childText:"子組件原始文本" } const mutations = {
    changeTestMsg(state, str){
        state.testMsg = str;
    },
    changeChildText(state, str){
        state.childText = str;
    }

} const store = new Vuex.Store({ state: state, mutations: mutations
}) export default store;

后記

通過該vuex示例,了解vuex的常用配置及方法調用。希望對不怎么熟悉vuex的同學快速上手vuex項目有點幫助。

因為沒太多東西,我自己也是剛接觸,本例就不往GitHub扔了,如果嘗試了本例,但是沒有跑起來的同學,可以一起交流下。

移動端列表查詢最佳實踐

seo達人

無論是 pc 端還是移動端,無可避免都會涉及到列表查詢有關的操作,但對于這兩種不同的設備,其列表查詢的最佳處理方式也是完全不同。

對于 pc 端列表查詢來說,前端通常是給與服務端當前需要獲取的數據量(如 pageCount,limit 等參數)以及所需要獲取數據的位置(如 pageSize,offset 等參數)作為查詢條件。然后服務端然后返回數據總數,以及當前數據,前端再結合這些數據顯示頁面總數等信息。這里我稱為相對位置取數。

對于移動端而言,沒有pc 端那么大的空間展示以及操作,所以基本上都會采用下拉取數這種方案。

那么我們在處理移動端列表查詢時候使用這種相對位置取數會有什么問題呢?

相對位置取數存在的問題

性能劣勢

通過相對位置取數會具有性能問題,因為一旦使用 offset 信息來獲取數據,隨著頁數的增加,響應速度也會變的越來越慢。因為在數據庫層面,我們每次所獲取的數據都是“從頭開始第幾條”,每次我們都需要從第一條開始計算,計算后舍棄前面的數據,只取最后多條數據返回前端。

當然了,對于相對位置取數來說,數據庫優化是必然的,這里我就不多做贅述了。對于前端開發來說,優秀的的查詢條件設計可以在一定方面解決此問題。

數據顯示重復

事實上,對于一個實際運行的項目而言,數據更新才是常態,如果數據更新的頻率很高或者你在當前頁停留的時間過久的話,會導致當前獲取的數據出現一定的偏差。

例如:當你在獲取最開始的 20 條數據后,正準備獲取緊接著的后 20 條數據時,在這段時間內 ,發生了數據增加,此時移動端列表就可能會出現重復數據。雖然這個問題在 pc 端也存在,但是 pc 端只會展示當前頁的信息,這樣就避免了該問題所帶來的負面影響。

結合列表 key 維持渲染正確

我們在上面的問題中說明了,移動端下拉加載中使用相對位置查詢取數是有問題的。

那么,如果當前不能迅速結合前后端進行修改 api 的情況下,當服務端傳遞過來的數據與用戶想要得的數據不一致,我們必須在前端進行處理,至少處理數據重復問題所帶來的負面影響。

因為當前分頁請求時無狀態的。在分頁取到數據之后前端可以對取得的數據進行過濾,過濾掉當前頁面已經存在的 key(例如 id 等能夠確定的唯一鍵)。

通過這種處理方式,我們至少可以保證當前用戶看到的數據不會出現重復。同時當列表數據可以編輯修改的時候,也不會出現因為 key 值相同而導致數據錯亂。

通過絕對位置獲取數據

如果不使用相對位置獲取數據,前端可以利用當前列表中的最后一條數據作為請求源參數。前端事先記錄最后一條數據的信息。例如當前的排序條件為創建時間,那么記錄最后一條數據的創建時間為主查詢條件(如果列表對應的數據不屬于個人,可能創建時間不能唯一決定當前數據位置,同時還需要添加 ID 等信息作為次要查詢條件)。

當我們使用絕對位置獲取數據時候,雖然我們無法提供類似于從第 1 頁直接跳轉 100 頁的查詢請求,但對于下拉加載這種類型的請求,我們不必擔心性能以及數據重復顯示的問題。

對于相對位置取數來說,前端可以根據返回數據的總數來判斷。但當使用絕對位置取數時,即使獲取數據總數,也無法判斷當前查詢是否存在后續數據。

從服務器端實現的角度來說,當用戶想要得到 20 條數據時候,服務端如果僅僅只向數據庫請求 20 條數據,是無法得知是否有后續數據的。服務端可以嘗試獲取當前請求的數據條數 + 1, 如向數據庫請求 21 條數據,如果成功獲得 21 條數據,則說明至少存在著 1 條后續數據,這時候,我們就可以返回 20 條數據以及具有后續數據的信息。但如果我們請求 21 條數據卻僅僅只能獲取 20 條數據(及以下),則說明沒有后續數據。

如可以通過 “hasMore” 字段來表示是否能夠繼續下拉加載的信息。

{ data: [], hasMore: true }

結合 HATEOAS 設計優化

事實上,前面我們已經解決了移動端處理列表查詢的問題。但是我們做的還不夠好,前端還需要結合排序條件來處理并提供請求參數,這個操作對于前端來說也是一種負擔。那么我們就聊一下 HATEOAS 。

HATEOAS (Hypermedia As The Engine Of Application State, 超媒體即應用狀態引起) 這個概念最早出現在 Roy Fielding 的論文中。REST 設計級別如下所示:

  • REST LEVEL 0: 使用 HTTP 作為傳輸方式
  • REST LEVEL 1: 引入資源的概念(每一個資源都有對應的標識符和表達)
  • REST LEVEL 2: 引入 HTTP 動詞(GET 獲取資源/POST 創建資源/PUT 更新或者創建字樣/DELETE 刪除資源 等)
  • REST LEVEL 3: 引入 HATEOAS (在資源的表達中包含了鏈接信息??蛻舳丝梢愿鶕溄觼戆l現可以執行的動作)

HATEOAS 會在 API 返回的數據中添加下一步要執行的行為,要獲取的數據等 URI 的鏈接信息??蛻舳酥灰@取這些信息以及行為鏈接,就可以根據這些信息進行接下來的操作。

對于當前的請求來說,服務端可以直接返回下一頁的信息,如

{ data: [], hasMore: true, nextPageParams: {}    
}

服務端如此傳遞數據,前端就不需要對其進行多余的請求處理,如果當前沒有修改之前的查詢以及排序條件,則只需要直接返回 “nextPageParams” 作為下一頁的查詢條件即可。

這樣做的好處不但符合 REST LEVEL 3,同時也減輕了前端的心智模型。前端無需配置下一頁請求參數。只需要在最開始查詢的時候提供查詢條件即可。

當然,如果前端已經實現了所有排序添加以及查詢條件由服務端提供,前端僅僅提供組件,那么該方案更能體現優勢。 前端是不需要知道當前業務究竟需要什么查詢條件,自然也不需要根據查詢條件來組織下一頁的條件。同時,該方案的輸入和輸出都由后端提供,當涉及到業務替換( 查詢條件,排序條件修改)時候,前端無需任何修改便可以直接替換和使用。

其他注意事項

一旦涉及到移動端請求,不可避免的會有網絡問題,當用戶在火車或者偏遠地區時候,一旦下拉就會涉及取數,但是當前數據沒有返回之前,用戶多次下拉可能會有多次取數請求,雖然前端可以結合 key 使得渲染不出錯,但是還是會在緩慢的網絡下請求多次,無疑雪上加霜。這時候我們需要增加條件變量 loading。

偽代碼如下所示:

// 查詢 function search(cond) {
  loading = true api.then(res => {
      loading = false }).catch(err => {
      loading = false })
} // 獲取下一頁數據 function queryNextPage() { if (!nextPageParams) return if (!loading) return search(nextPageParams)
}

前端架構演進及主流UI

前端達人

文章目錄



    前端三要素

    HTML(結構):超文本標記語言(Hyper Text Markup Language),決定網頁的結構和內容
    CSS(表現):層疊樣式表(Cascading Style Sheets),設定網頁的表現樣式
    JavaScript(行為):是一種弱類型腳本語言,其源代碼不需經過編譯,而是由瀏覽器解釋運行, 用于控制網頁的行為
    HTML 稱為超文本標記語言,是一種標識性的語言。它通過一系列標簽組合,組成一個個不同結構的頁面!關于html標簽的學習可以去菜鳥教程學習,此處不再贅述!

    CSS層疊樣式表 也是一門標記語言,并不是編程語言,因此不可以自定義變量,不可以引用等,換句話說
    就是不具備任何語法支持,它主要缺陷如下:

    語法不夠強大,比如無法嵌套書寫,導致模塊化開發中需要書寫很多重復的選擇器;
    沒有變量和合理的樣式復用機制,使得邏輯上相關的屬性值必須以字面量的形式重復輸出,導致難 以維護;
    這就導致了我們在工作中無端增加了許多工作量。為了解決這個問題,前端開發人員會使用一種稱之為 【CSS 預處理器】 的工具,提供 CSS 缺失的樣式層復用機制、減少冗余代碼,提高樣式代碼的可維護 性。大大提高了前端在樣式上的開發效率。

    什么是CSS 預處理器呢?

    CSS 預處理器定義了一種新的語言,其基本思想是,用一種專門的編程語言,為 CSS 增加了一些編程的 特性,將 CSS 作為目標生成文件,然后開發者就只要使用這種語言進行 CSS 的編碼工作。轉化成通俗易 懂的話來說就是“用一種專門的編程語言,進行 Web 頁面樣式設計,再通過編譯器轉化為正常的 CSS 文 件,以供項目使用”。

    常用的 CSS 預處理器有哪些?

    SASS:基于 Ruby,通過服務端處理,功能強大。解析效率高。需要學習 Ruby 語言,上手難度高于 LESS。
    LESS:基于 NodeJS,通過客戶端處理,使用簡單。功能比 SASS 簡單,解析效率也低于 SASS,但在實際開發中足夠了,所以我們后臺人員如果需要的話,建議使用 LESS。
    JavaScript 一門弱類型腳本語言,其源代碼在發往客戶端運行之前不需經過編譯,而是將文本格式的字 符代碼發送給瀏覽器由瀏覽器解釋運行。

    Native 原生 JS 開發
    原生 JS 開發,也就是讓我們按照 【ECMAScript】 標準的開發方式,簡稱是 ES,特點是所有瀏覽器都支持。

    ES 標準已發布如下版本:

    ES3
    ES4(內部,未正式發布)
    ES5(全瀏覽器支持)
    ES6(常用,當前主流版本:webpack打包成為ES5支持!)
    ES7
    ES8
    ES9(草案階段)
    從 ES6 開始每年發布一個版本,以年份作為名稱,區別就是逐步增加新特性。

    TypeScript 微軟的標準
    TypeScript 是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,而且本質上向這 個語言添加了可選的靜態類型和基于類的面向對象編程。由安德斯·海爾斯伯格(C#、Delphi、 TypeScript 之父;.NET 創立者)主導。

    JavaScript 框架

    1.jQuery庫

    大家最熟知的 JavaScript庫,優點是簡化了 DOM 操作,缺點是 DOM 操作太頻繁,影響前端性能;在 前端眼里使用它僅僅是為了兼容 IE6、7、8;

    2.Angular庫

    Google 收購的前端框架,由一群 Java 程序員開發,其特點是將后臺的 MVC 模式搬到了前端并增加了模 塊化開發的理念,與微軟合作,采用 TypeScript 語法開發;對后臺程序員友好,對前端程序員不太友 好;最大的缺點是版本迭代不合理(如:1代 -> 2代,除了名字,基本就是兩個東西;已推出了 Angular6)

    3.React

    Facebook 出品,一款高性能的 JS 前端框架;特點是提出了新概念 【虛擬 DOM】 用于減少真實 DOM 操作,在內存中模擬 DOM 操作,有效的提升了前端渲染效率;缺點是使用復雜,因為需要額外學習一 門 【JSX】 語言;

    4.Vue

    一款漸進式 JavaScript 框架,所謂漸進式就是逐步實現新特性的意思,如實現模塊化開發、路由、狀態 管理等新特性。

    其特點是綜合了 Angular(模塊化) 和 React(虛擬 DOM) 的優點;

    5.Axios

    前端通信框架;因為 Vue 的邊界很明確,就是為了處理 DOM,所以并不具備通信能力,此時就需要額 外使用一個通信框架與服務器交互;當然也可以直接選擇使用 jQuery 提供的 A JAX 通信功能;

    JavaScript 構建工具

    Babel:JS 編譯工具,主要用于瀏覽器不支持的 ES 新特性,比如用于編譯 TypeScript
    WebPack:模塊打包器,主要作用是打包、壓縮、合并及按序加載

    NodeJs


    Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環境,說白了就是運行在服務端的JavaScript;

    前端人員為了方便開發也需要掌握一定的后端技術,但我們 Java 后臺人員知道后臺知識體系極其龐大復 雜,所以為了方便前端人員開發后臺應用,就出現了 NodeJS 這樣的技術。NodeJS 的作者已經聲稱放棄 NodeJS(說是架構做的不好再加上笨重的node_modules,可能讓作者不爽了吧),開始開發全新架構的 什么是Deno?跟Node.js有何區別?

    既然是后臺技術,那肯定也需要框架和項目管理工具,NodeJS 框架及項目管理工具如下:

    Express: NodeJS 框架
    Koa: Express 簡化版
    NPM: 項目綜合管理工具,類似于 Maven
    YARN: NPM 的替代方案,類似于 Maven 和 Gradle 的關系

    常用UI框架


    Ant-Design:阿里巴巴出品,基于 React 的 UI 框架
    ElementUI、MintUi、iview、ic、:餓了么出品,基于 Vue 的 UI 框架
    Bootstrap:Twitter 推出的一個用于前端開發的開源工具包
    AmazeUI:又叫“妹子 UI”,一款 HTML5 跨屏前端框架
    Layui:輕量級框架(Layer)
    Ant-Design

    ant.design是基于react開發的一個解放ui和前端的工具,它提供了一致的設計方便我們快速開發和減少不必要的設計與代碼,很多實用react框架的開發者都已經在使用ant.design了,且其在github上的star數也早已上萬,足見其火熱程度。

    ant.design的目的也在于提高用戶、開發者等多方的體驗與幸福感。

    ant.design設計很精妙,vue的iview就是模仿ant.design來實現的

    官網地址:https://ant.design/index-cn
    github地址:https://github.com/ant-design/ant-design/
    ElementUi

    ElementUi是餓了么前端開源維護的VueUI組件庫,組件齊全基本涵蓋后臺所需的所有組件,文檔講解詳細,例子也很豐富。主要用于開發PC端的頁面,是一個質量比較高的VueUI組件庫!

    官網地址:http://element-cn.eleme.io/#/zh-CN
    github地址:https://github.com/ElementUI/element-starter
    vue-element-admin:https://github.com/PanJiaChen/vue-element-admin
    MintUi

    MintUi是由餓了么前端團隊推出的一個基于 Vue.js的移動端組件庫,組件比較單一,功能簡單易上手!

    官網地址:https://mint-ui.github.io/#!/zh-cn
    github地址:https://github.com/ElemeFE/mint-ui
    iview

    iview 是一個強大的基于 Vue 的 UI 庫,有很多實用的基礎組件比 elementui 的組件更豐富,主要服務于 PC 界面的中后臺產品。使用單文件的 Vue 組件化開發模式 基于 npm + webpack + babel 開發,支持 ES2015 高質量、功能豐富 友好的 API ,自由靈活地使用空間。

    官網地址:https://www.iviewui.com/
    github地址:https://github.com/TalkingData/iview-weapp
    iview-admin: https://github.com/iview/iview-admin
    備注:屬于前端主流框架,選型時可考慮使用,主要特點是移動端支持較多

    ICE

    飛冰是阿里巴巴團隊基于 React/Angular/Vue 的中后臺應用解決方案,在阿里巴巴內部,已經有 270 多 個來自幾乎所有 BU 的項目在使用。飛冰包含了一條從設計端到開發端的完整鏈路,幫助用戶快速搭建 屬于自己的中后臺應用。

    官網地址:https://alibaba.github.io/ice
    github地址 :https://github.com/alibaba/ice
    備注:主要組件還是以 React 為主,對 Vue 的支持還不太完善, 目前尚處于觀望階段

    VantUI

    Vant UI 是有贊前端團隊基于有贊統一的規范實現的 Vue 組件庫,提供了一整套 UI 基礎組件和業務組 件。通過 Vant,可以快速搭建出風格統一的頁面,提升開發效率。

    官網地址: https://youzan.github.io/vant/#/zh-CN/intro
    github地址: https://github.com/youzan/vant
    AtUi

    at-ui是一款基于Vue 2.x的前端UI組件庫,主要用于快速開發PC網站產品。 它提供了一套npm + webpack + babel 前端開發工作流程,CSS樣式獨立,即使采用不同的框架實現都能保持統一的 UI風格。

    官網地址:https://at-ui.github.io/at-ui/#/zh
    github地址: https://github.com/at-ui/at-ui
    CubeUI
    cube-ui 是滴滴團隊開發的基于 Vue.js 實現的精致移動端組件庫。支持按需引入和后編譯,輕量靈活; 擴展性強,可以方便地基于現有組件實現二次開發.

    官網地址:https://didi.github.io/cube-ui/#/zh-CN
    github地址:https://github.com/didi/cube-ui/
    Flutter

    Flutter 是谷歌的移動端 UI 框架,可在極短的時間內構建 Android 和 iOS 上高質量的原生級應用。 Flutter 可與現有代碼一起工作, 它被世界各地的開發者和組織使用, 并且 Flutter 是免費和開源的。

    官網地址:https://flutter.dev/docs
    github地址:https://github.com/flutter/flutter
    備注:Google 出品,主要特點是快速構建原生 APP 應用程序,如做混合應用該框架為必選框架

    Ionic

    Ionic 既是一個 CSS 框架也是一個 Javascript UI 庫,Ionic 是目前最有潛力的一款 HTML5 手機應用開發 框架。通過 SASS 構建應用程序,它提供了很多 UI 組件來幫助開發者開發強大的應用。它使用 JavaScript MVVM 框架和 AngularJS/Vue 來增強應用。提供數據的雙向綁定,使用它成為 Web 和移動 開發者的共同選擇。

    官網地址:https://ionicframework.com/
    github地址:https://github.com/ionic-team/ionic
    mpvue

    mpvue 是美團開發的一個使用 Vue.js 開發小程序的前端框架,目前支持 微信小程序、百度智能小程 序,頭條小程序 和 支付寶小程序。 框架基于 Vue.js,修改了的運行時框架 runtime 和代碼編譯器 compiler 實現,使其可運行在小程序環境中,從而為小程序開發引入了 Vue.js 開發體驗。

    官網地址:http://mpvue.com/
    github地址:https://github.com/Meituan-Dianping/mpvue
    備注:完備的 Vue 開發體驗,并且支持多平臺的小程序開發,推薦使用

    WeUi

    WeUI 是一套同微信原生視覺體驗一致的基礎樣式庫,由微信官方設計團隊為微信內網頁和微信小程序 量身設計,令用戶的使用感知更加統一。包含 button、cell、dialog、toast、article、icon 等各式元 素。

    官網地址:https://weui.io/
    github地址:https://github.com/weui/weui.git

    前后端分離的演進

    為了降低開發的復雜度,以后端為出發點,比如:Struts、SpringMVC 等框架的使用,就是后端的 MVC 時代;

    以 SpringMVC 流程為例:


    1.發起請求到前端控制器(DispatcherServlet)
    2.前端控制器請求HandlerMapping查找 Handler (可以根據xml配置、注解進行查找)
    3.處理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象,多個HandlerInterceptor攔截器對象),通過這種策略模式,很容易添加新的映射策略
    4.前端控制器調用處理器適配器去執行Handler
    5.處理器適配器HandlerAdapter將會根據適配的結果去執行Handler
    6.Handler執行完成給適配器返回ModelAndView
    7.處理器適配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一個底層對象,包括 Model和view)
    8.前端控制器請求視圖解析器去進行視圖解析 (根據邏輯視圖名解析成真正的視圖(jsp)),通過這種策略很容易更換其他視圖技術,只需要更改視圖解析器即可
    9.視圖解析器向前端控制器返回View
    10.前端控制器進行視圖渲染 (視圖渲染將模型數據(在ModelAndView對象中)填充到request域)
    11.前端控制器向用戶響應結果
    優點:

    MVC 是一個非常好的協作模式,能夠有效降低代碼的耦合度,從架構上能夠讓開發者明白代碼應該寫在 哪里。為了讓 View 更純粹,還可以使用 Thymeleaf、Freemarker 等模板引擎,使模板里無法寫入 Java 代碼,讓前后端分工更加清晰。單體應用!

    缺點:

    前端開發重度依賴開發環境,開發效率低,這種架構下,前后端協作有兩種模式:

    1、第一種是前端寫 DEMO,寫好后,讓后端去套模板。好處是 DEMO 可以本地開發,很。不足是 還需要后端套模板,有可能套錯,套完后還需要前端確定,來回溝通調整的成本比較大;

    2、另一種協作模式是前端負責瀏覽器端的所有開發和服務器端的 View 層模板開發。好處是 UI 相關的 代碼都是前端去寫就好,后端不用太關注,不足就是前端開發重度綁定后端環境,環境成為影響前端開 發效率的重要因素。

    前后端職責糾纏不清:模板引擎功能強大,依舊可以通過拿到的上下文變量來實現各種業務邏輯。但這樣只要前端弱勢一點,往往就會被后端要求在模板層寫出不少業務代碼。還有一個很大的灰色地帶是,頁面路由等功能本應該是前端最關注的,但卻是由后端來實現。

    ajax 的時代

    時間回到 2005 年 AJAX (Asynchronous JavaScript And XML,異步 JavaScript 和 XML,老技術新 用法) 被正式提出并開始使用 CDN 作為靜態資源存儲,于是出現了 JavaScript 王者歸來(在這之前 JS 都是用來在網頁上貼狗皮膏藥廣告的)的 SPA(Single Page Application)單頁面應用時代。
    優點:
    這種模式下,前后端的分工非常清晰,前后端的關鍵協作點是 A JAX 接口。看起來是如此美妙,但回過 頭來看看的話,這與 JSP 時代區別不大。復雜度從服務端的 JSP 里移到了瀏覽器的 JavaScript,瀏覽器 端變得很復雜。類似 Spring MVC,這個時代開始出現瀏覽器端的分層架構:

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fP8yZYUq-1587440620216)()]
    缺點:

    前后端接口的約定: 如果后端的接口一塌糊涂,如果后端的業務模型不夠穩定,那么前端開發會很 痛苦;不少團隊也有類似嘗試,通過接口規則、接口平臺等方式來做。有了和后端一起沉淀的 接口 規則,還可以用來模擬數據,使得前后端可以在約定接口后實現并行開發。
    前端開發的復雜度控制: SPA 應用大多以功能交互型為主,JavaScript 代碼過十萬行很正常。大量 JS 代碼的組織,與 View 層的綁定等,都不是容易的事情
    前端為主的 MV* 時代

    此處的 MV* 模式如下:

    MVC(同步通信為主):Model、View、Controller
    MVP(異步通信為主):Model、View、Presenter
    MVVM(異步通信為主):Model、View、ViewModel
    為了降低前端開發復雜度,涌現了大量的前端框架,比如: AngularJS 、 React 、Vue.js 、 EmberJS 等,這些框架總的原則是先按類型分層,比如 Templates、Controllers、Models,然后再在層內做切分,




    優點:

    前后端職責很清晰: 前端工作在瀏覽器端,后端工作在服務端。清晰的分工,可以讓開發并行,測 試數據的模擬不難,前端可以本地開發。后端則可以專注于業務邏輯的處理,輸出 RESTful等接 口。
    前端開發的復雜度可控: 前端代碼很重,但合理的分層,讓前端代碼能各司其職。這一塊蠻有意思 的,簡單如模板特性的選擇,就有很多很多講究。并非越強大越好,限制什么,留下哪些自由,代 碼應該如何組織,所有這一切設計,得花一本書的厚度去說明。
    -部署相對獨立: 可以快速改進產品體驗
    缺點:

    代碼不能復用。比如后端依舊需要對數據做各種校驗,校驗邏輯無法復用瀏覽器端的代碼。如果可 以復用,那么后端的數據校驗可以相對簡單化。
    全異步,對 SEO 不利。往往還需要服務端做同步渲染的降級方案。 性能并非最佳,特別是移動互聯網環境下。
    SPA 不能滿足所有需求,依舊存在大量多頁面應用。URL Design 需要后端配合,前端無法完全掌控。
    NodeJS 帶來的全棧時代

    前端為主的 MV* 模式解決了很多很多問題,但如上所述,依舊存在不少不足之處。隨著 NodeJS 的興 起,JavaScript 開始有能力運行在服務端。這意味著可以有一種新的研發模式:
    ————————————————
    版權聲明:本文為CSDN博主「叁有三分之一」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
    原文鏈接:https://blog.csdn.net/iME_cho/article/details/105654633


【CSS基礎學習】行內元素,塊級元素,行內塊級元素

前端達人

文章目錄

    • 元素的顯示方式和轉換


    • 元素的顯示方式和轉換

      塊級元素

      塊級元素(inline):
      塊級元素可以包含行內元素和其它塊級元素,且占據父元素的整個空間,可以設置 width 和 height 屬性,瀏覽器通常會在塊級元素前后另起一個新行。
      常見塊級元素:

      header,form,ul,ol,table,article,div,hr,aside,figure,canvas,video,audio,footer
      特點:

      塊級元素會獨占一行
      高度,行高,外邊距和內邊距都可以單獨設置
      寬度默認是容器的100%
      可以容納內聯元素和其他的塊級元素
      例如:





      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
          <style>
              div{
                  width: 150px;
                  height: 150px;
                  background-color: cadetblue;
              }
          </style>
      </head>
      <body>
          <div>塊級元素1</div>
          <div>塊級元素2</div>
      </body>
      </html>
      



       

      分析:
      塊級元素的高和寬可以被修改,而且塊級元素會在一個塊級元素之后另起一行。

      行級元素
      行級元素(block):
      一般情況下,行內元素只能包含內容或者其它行內元素,寬度和長度依據內容而定,不可以設置,可以和其它元素和平共處于一行。
      常見行級元素:
      a,b,strong,span,img,label,button,input,select,textarea
      特點:

      和相鄰的行內元素在一行上
      高度和寬度無效,但是水平方向上的padding和margin可以設置,垂直方向上的無效
      默認的寬度就是它本身的寬度
      行內元素只能容納純文本或者是其他的行內元素(a標簽除外)
      例如:

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
          <style>
              span{
                  width: 150px;
                  height: 150px;
                  font-size: 40px;
                  background-color: cadetblue;
              }
          </style>
      </head>
      <body>
          <span>行級元素1</span>
          <span>行級元素2</span>
      </body>
      </html>
      


      分析:
      對他的高和寬進行修改,但是沒有發生改變,對他的字體大小進行修改卻發生了整體大小的改變,所以得出結論行級元素的寬高是與內容有關的,且不可修改高寬的屬性,只能對內容修改。

      行內塊級元素
      行內塊級元素(inline-block):
      他包含了行級元素與塊級元素的特點,在同一行顯示,可以設置元素寬度和高度,可以將塊級元素和行級元素轉化為行內塊級元素。他不屬于基本的元素,是通過修改獲得的。
      特點:

      和其他行內或行內塊級元素元素放置在同一行上
      元素的高度、寬度、行高以及頂和底邊距都可設置
      舉例:
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
          <style>
              span{
                  width: 150px;
                  height: 150px;
                  font-size: 20px;
                  background-color: cadetblue;
                  display: inline-block;
              }
          </style>
      </head>
      <body>
          <span>以前我是行級元素,</span>
          <span>現在我只想做行內塊級元素。</span>
      </body>
      </html>
      


      分析:
      他可以進行修改寬高,也屬于同一行,包含著行級元素和塊級元素的特點,他就是行!內!塊!級!元!素!

      顯示方式之間的轉化
      想要轉成什么顯示方式 格式
      塊級元素 display:inline;
      行級元素 display: block;
      行內塊級元素 display: inline-block;
      這些直接在元素里面添加就可以了,就會轉換成相對應的格式。
      舉例:


      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
          <style>
              div{
                  width: 150px;
                  height: 150px;
                  font-size: 30px;
                  background-color: cadetblue;
                  display: inline;
              }
          </style>
      </head>
      <body>
          <div>我以前是塊級元素,</div>
          <div>現在我是行級元素!</div>
      </body>
      </html>
      






      分析:
      在VSC中,修改寬高的代碼已經出現了波浪線,證明他是錯誤的,所以現在的div已經變成了行級元素。






      ————————————————
      版權聲明:本文為CSDN博主「董小宇」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
      原文鏈接:https://blog.csdn.net/lolly1023/article/details/105715892



帶你快速了解VSCode的10個特性,極大提高開發效率

前端達人

其實VSCode編輯器本身自帶了一個功能(Interactive Editor Playground :可以讓你快速了解VSCode的特性,并且是可以交互的),



但很可惜它的內容是全英文的(將VSCode設置為中文也沒用哦~),



我將每一部分截圖下來,并為你說明關鍵內容,教你學會使用 Interactive Editor Playground



還有一些顯而易見的特性,我不會再用文字敘述一遍(它們都是潛移默化的)

在下文中會涉及到大量快捷鍵的介紹,如果看不懂快捷鍵請自行百度

鼠標 = 文本光標 = 光標

本文成于2020年4月22日,隨著VSCode的版本更迭,此部分內容可能會略有差異(小更改不影響觀看,有較大影響的更新請在評論區告之,我會及時更新的)



打開VSCode > Help > Interactive Playground

點擊查看原圖

你將會打開 Interactive Editor Playground 頁面

互動式編輯游樂場

點擊查看原圖

VS代碼中的核心編輯器包含許多特性。此頁高亮顯示了10個特性,每個特性介紹中都提供了代碼行供你編輯

接下來的10行內容(你可以理解為目錄,對應10個特性)

多光標編輯(Multi-Cursor Editing)- 選擇一塊區域,選擇所有匹配項,添加其余光標等
智能感應(intelliSense)- 獲取代碼和外部模塊的代碼幫助和參數建議
行操作(Line Actions )- 快速移動行以重新排序代碼
重命名重構(Rename Refactoring)- 快速重命名代碼庫中的符號(比如變量名、函數名)
格式化(Formatting)- 使用內置文檔和選擇格式使代碼看起來很棒
代碼折疊(Code Folding) - 通過折疊其他代碼區域,關注代碼中最相關的部分
錯誤和警告(Errors and Warnings)- 寫代碼時請參閱錯誤和警告
片段(Snippets)- 花更少的時間輸入片段
Emmet - 只需要敲一行代碼就能生成你想要的完整HTML結構等(極大方便前端開發)
JavaScript Type Checking- 使用零配置的TypeScript對JavaScript文件執行類型檢查。
Multi-Cursor Editing

點擊查看原圖

使用多光標編輯可以同時編輯文檔的多個部分,極大地提高了工作效率

框式選擇
鍵盤同時按下 Shift + DownArrow(下鍵)、Shift + RightArrow(右鍵)、Shift + UpArrow(上鍵)、Shift + LeftArrow(左鍵) 的任意組合可選擇文本塊
也可以用鼠標選擇文本時按 Shift + Alt 鍵
或使用鼠標中鍵拖動選擇(可用性很高)
添加光標
按 Ctrl + Alt + UpArrow 在行上方添加新光標
或按 Ctrl + Alt + DownArrow 在行下方添加新光標
您也可以使用鼠標和 Alt + Click 在任何地方添加光標(可用性很高)
在所有出現的字符串上創建光標
選擇字符串的一個實例,例如我用鼠標選中所有background,然后按 Ctrl + Shift + L,文本中所有的background都將被選中(可用性很高)
IntelliSense

點擊查看原圖

Visual Studio Code 預裝了強大的JavaScript和TypeScript智能感知。

在代碼示例中,將文本光標放在錯誤下劃線的上面,會自動調用IntelliSense


這只是智能提示的冰山一角,還有懸停在函數名上可以看到參數及其注釋(如果有)等等,它會潛移默化的帶給你極大幫助

其他語言在安裝對應插件后,會附帶對應語言的IntelliSense

Line Actions

點擊查看原圖

分別使用 Shift + Alt + DownArrow 或 Shift + Alt + UpArrow 復制光標所在行并將其插入當前光標位置的上方或下方
分別使用 Alt + UpArrow 和 Alt + DownArrow 向上或向下移動選定行(可用性很高)
用 Ctrl + Shift + K 刪除整行(可用性很高)
通過按 Ctrl + / 來注釋掉光標所在行、切換注釋(可用性很高)
Rename Refactoring

點擊查看原圖

重命名符號(如函數名或變量名)

  1. 將光標選中符號,按F2鍵
  2. 或者 選中該符號,鼠標右鍵 > Rename Symbol

重命名操作將在項目中的所有文件中發生可用性很高

Formatting

點擊查看原圖

代碼如果沒有良好的編寫格式,閱讀起來是一個折磨

Formatting可以解決編寫格式問題:無論你的代碼的格式寫的有多么糟糕,它可以將代碼格式化為閱讀性良好的格式

格式化整個文檔 Shift + Alt + F (可用性很高)
格式化當前行 Ctrl + K Ctrl + F(即先按Ctrl,再按K,最后按F)
鼠標右鍵 > Format Document (格式化整個文檔)
將格式化操作設置為自動化(保存時自動格式化整個文檔):Ctrl + , 輸入 editor.formatOnSave

點擊查看原圖

Code Folding

點擊查看原圖

鼠標操作,自己嘗試一下,秒懂

快捷鍵:

  • 折疊 Ctrl + Shift + [
  • 展開 Ctrl + Shift + ]

折疊代碼段是基于基于縮進

Errors and Warning

點擊查看原圖

錯誤和警告將在你出現錯誤時,高亮該代碼行

在代碼示例中可以看到許多語法錯誤(如果沒有,請你隨便修改它,讓它出現錯誤)

按F8鍵可以按順序在錯誤之間導航,并查看詳細的錯誤消息(可用性很高)

Snippets

通過使用代碼片段,可以大大加快編輯速度

在代碼編輯區,你可以嘗試輸入try并從建議列表中選擇try catch,

然后按Tab鍵或者Enter,創建try->catch塊

你的光標將放在文本error上,以便編輯。如果存在多個參數,請按Tab鍵跳轉到該參數。

Emmet

Emmet將代碼片段的概念提升到了一個全新的層次(前端開發的大寶貝)

你可以鍵入類似Css的可動態解析表達式,并根據在abrevision中鍵入的內容生成輸出

比如說:

然后Enter

JavaScript Type Checking

點擊查看原圖



————————————————
版權聲明:本文為CSDN博主「索兒呀」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Zhangguohao666/article/details/105676173

規范git commit的提交記錄

seo達人

隨著項目體積的增加,參與到項目中的同學越來越多,每個人都有自己的打 git log 的習慣:

  • 格式 1: add: 添加...
  • 格式 2: [add]: 添加...
  • 格式 3: Add 添加...

為了形成統一的規范,達成共識,從而降低協作開發成本,需要對 git commit 記錄進行規范。

規范 git commit 記錄

規范 git commit 記錄,需要做兩件事情:

  • 通過交互式命令行,自動生成符合指定規范的 commit 記錄
  • 提交記錄后,在 git hooks 中進行 commit 記錄格式檢查
問:既然已經交互式生成了規范記錄,為什么需要在 hooks 進行檢查?

交互式生成 commit 記錄,需要用戶調用自定義的 npm scripts,例如npm run commit。但還是可以直接調用原生 git 命令 git commit 來提交記錄。而檢查是在正式提交前進行的,因此不符合要求的記錄不會生效,需要重新 commit。

調研:交互式 commit log 規范方案

前期調研結果,關于 commit 提示有兩種做法:

  1. 直接使用 commitizen 中常用的 adapter
  2. 根據團隊的需要,自定義 adapter

方法 1 的優缺點:

優點 1: 直接安裝對應的 adapter 即可

優點 2: 無開發成本

缺點 1: 無法定制,不一定滿足團隊需要

方法 2 的優缺點:

優點 1: 可定制,滿足開發需求

優點 2: 單獨成庫,發布 tnpm,作為技術建設

缺點 1: 需要單獨一個倉庫(但開發成本不高)

代碼實現

在實際工作中,發現方法 1 中的常用規范,足夠覆蓋團隊日常開發場景。所以,選擇了方法 1.

step1: 安裝 npm 包

npm i --save-dev commitizen cz-conventional-changelog @commitlint/cli @commitlint/config-conventional husky

添加 package.json 的配置:

"scripts": { "commit": "git-cz" }, "husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" }
}, "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" }
}

在項目根目錄下創建commitlint.config.js

module.exports = { extends: ["@commitlint/config-conventional"]
};

使用方法:不再使用git commit -m ...,而是調用npm run commit。

<img src="https://tva1.sinaimg.cn/large/006tNbRwly1gbjcfr3xb5j30cw00tjrd.jpg" style="width: 100% !important;"/>

使用scss開發小程序(各種小程序平臺通用)

seo達人

微信小程序的wxss、阿里旗下淘寶、支付寶小程序的acss等等語法很類似原生css,但是在web開發里用慣了動態css語言,再寫回原生css很不習慣,尤其是父子樣式的嵌套寫法非常繁瑣。

因此,我希望能有一個自動化構建方案,能夠簡單地將scss轉換成小程序的樣式語言。

方案1

以前寫微信小程序的依賴庫時用過,使用gulp編譯,將源碼和編譯后的代碼分別放到src和dist兩個目錄。gulp會處理src下面的所有文件,將其中的scss轉換成css,并將其他所有文件原封不動挪到dist下相應位置。

這里就不詳細說了,代碼參考Wux

方案2

非常簡單直接,使用Webstorm/IDEAFile Watchers功能實時轉換。

安裝Ruby和sass

確保命令行輸入sass -v能出現版本號,安裝過程略。

安裝File Watchers

到插件市場上搜索并安裝(已安裝則跳過)

1.png

添加scss的轉換腳本

現在安裝完插件打開項目會自動彈出scss轉css的向導,方便了很多。但還需要做一些修改,配置如下:

2.png

首先要將生成文件的后綴名改掉,比如這里我的淘寶小程序就得是acss。

其次,將Arguments改為:

$FileName$:$FileNameWithoutExtension$.acss --no-cache --sourcemap=none --default-encoding utf-8 --style expanded

如果不加--no-cache,scss文件同目錄下會出現一個.sass-cache目錄。

如果不加--sourcemap=none, scss文件同目錄下會出現一個.map文件。

如果不加--default-encoding utf-8, scss文件如果有中文注釋轉換就會報錯。

style可不加,這里用的是無縮進和壓縮的風格,反正小程序打包發布時還會壓,這里保持可讀性。

現在這個scss轉換是單獨作用于項目的,如果新建一個小程序項目,就需要重新添加(不建議設置成global,容易誤傷)。

注意到File Watchers列表的右側操作欄下方有導入導出按鈕,可以將現在配好的設置導出保存,將來新建項目時只要導入一下就行了。


之后還有一個問題,如果我手動將編譯后的css(即wxss或者acss,下略)文件刪除,scss文件不改動的話,就不會重新編譯出css文件。
或者萬一監聽失效或者不夠及時,css還有可能是舊的。
所以還需要一個命令,用來將整個目錄下的scss文件統一轉換,確保沒有遺漏和保持代碼。

不過我看了半天sasssass-convert的文檔,沒有找到一個可用的寫法,能讓命令行遍歷指定目錄下的所有scss文件,將其轉換成css放到源文件所在目錄,并且將后綴名改為wxss或者acss。

所以遍歷這個行為只能交給nodejs來實現,代碼如下:

創建編譯腳本build/scss-convert.js

var path = require("path") var fs = require("fs") const { exec } = require('child_process') const basePath = path.resolve(__dirname, '../') function mapDir(dir, callback, finish) {
  fs.readdir(dir, function(err, files) { if (err) { console.error(err) return }
    files.forEach((filename, index) => { let pathname = path.join(dir, filename)
      fs.stat(pathname, (err, stats) => { // 讀取文件信息 if (err) { console.log('獲取文件stats失敗') return } if (stats.isDirectory()) {
          mapDir(pathname, callback, finish)
        } else if (stats.isFile()) { if (!['.scss'].includes(path.extname(pathname))) { return }
          callback(pathname)
        }
      }) if (index === files.length - 1) {
        finish && finish()
      }
    })
  })
}

mapDir(
  basePath, function (file) { const newFileWithoutExt = path.basename(file, '.scss') if (newFileWithoutExt.startsWith('_')) { return // 按照scss規則,下劃線開頭的文件不會生成css } // exec可以讓nodejs執行外部命令 exec(`sass --no-cache --sourcemap=none --default-encoding utf-8 --style expanded ${file}:${newFileWithoutExt}.acss`, { cwd: path.dirname(file) // 不寫這個會導致生成的文件出現在根目錄 }, (err, stdout, stderr) => { if (err) { console.log(err) return } console.log(`stdout: ${stdout}`)
    })
  }, function() { // console.log('xxx文件目錄遍歷完了') }
)

package.json里添加一條script:

 "scripts": { "scss": "node build/scss-convert",
  },

ES6數據的解構賦值使用及應用

前端達人


定義


ES6 允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構(Destructuring)



本質上,這種寫法屬于“模式匹配”,只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值

如果解構不成功,變量的值就等于undefined

解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉為對象。由于undefined和null無法轉為對象,所以對它們進行解構賦值,都會報錯、



解構賦值的用途:

交換變量的值

例如:let x=1,y=2;[x,y] = [y,x]



從函數返回多個值

函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里返回。有了解構賦值,取出這些值就非常方便



函數參數的定義

解構賦值可以方便地將一組參數與變量名對應起來



提取 JSON 數據,很多接口數據只需要其中某部分

例如aa.axios.get(res=>{let {data:result}=res;}),則res.data.result = result了



函數參數的默認值

指定參數的默認值,就避免了在函數體內部再寫var foo = config.foo || ‘default foo’;這樣的語句



遍歷 Map 結構

Map 結構原生支持 Iterator 接口,配合變量的解構賦值,獲取鍵名和鍵值就非常方便



輸入模塊的指定方法

加載模塊時,往往需要指定輸入哪些方法。解構賦值使得輸入語句非常清晰。* const { SourceMapConsumer, SourceNode } = require(“source-map”);


1、數組的解構賦值


左右兩側數據解構須得吻合,或者等號左邊的模式,只匹配一部分的等號右邊的數組(屬于不完全解構)



特殊情況使用…擴展運算符,無值是空數組



左右兩邊等式的性質要相同,等號的右邊不是數組(或者嚴格地說,不是可遍歷的結構),那么將會報錯,只要某種數據結構具有 Iterator



接口,都可以采用數組形式的解構賦值,例如Set結構



解構賦值允許指定默認值,當一個數組成員嚴格等于undefined,默認值才會生效,否則取賦值的值;如果默認值是一個表達式,那么這個表達式是惰性求值的,即只有在用到的時候,才會求值;默認值可以引用解構賦值的其他變量,但該變量必須已經聲明



// 數組的解構賦值
 let [a,b] = [1,2];
 console.log([a,b],a);//[1, 2] 1
 let [aa] = [11,22];
 console.log(aa)//11
 let [aaa,bbb] = [111];
 console.log(aaa,bbb)//111 undefined
 let [head, ...tail] = [1, 2, 3, 4];
 console.log(head,tail)//1,[2,3,4]
 let [x, y, ...z] = ['a'];
 console.log(x,y,z)//a undefined []
 // 等號右邊不是數組會報錯
 // let [ab] = 121;
 // conosle.log(ab)//TypeError: 121 is not iterable
 // let [abc] = {}
 // conosle.log(abc)//TypeError: {} is not iterable
 // 默認值賦值
 let [zz = 1] = [undefined];
 console.log(zz)//1
 let [zzz = 1] = [null];
 console.log(zzz)//null
 let [foo = true] = [];
 console.log(foo)// true
 let [xxx, yyy = 'b'] = ['a'];
 console.log(xxx,yyy)//a,b
 let [xxxx, yyyy = 'b'] = ['a', undefined]; 
 console.log(xxxx,yyyy)//a,b
 function f() {
   console.log('aaa');
 }
 let [xx = f()] = [1];
 console.log(xx)//1
 let [qq=ww,ww=11] = [23,44];
 console.log(qq,ww)//23,44,因為ww申明比qq晚所以是undefined;

2、對象的解構賦值
對象的解構賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。真正被賦值的是后者,而不是前者

數組是按照位置區分,對象則是按照鍵名區分的,同樣的解構失敗則為undefine
可將已有方法對象解構賦值
嵌套賦值,注意是變量是否被賦值是模式還是鍵值
對象的解構賦值可以取到繼承的屬性
如果要將一個已經聲明的變量用于解構賦值,必須非常小心
let xx; // {xx} = {xx: 1}這樣會報錯,

解構賦值允許等號左邊的模式之中,不放置任何變量名。因此,可以寫出非常古怪的賦值表達式
({} = [true, false]);//可執行

由于數組本質是特殊的對象,因此可以對數組進行對象屬性的解構

objFuc(){
            // 對象解構賦值
            let {b,a} = {a:1}
            console.log(a,b)//1 undefined
            // 已有對象解構賦值
            let { sin, cos } = Math;//將Math對象的對數、正弦、余弦三個方法,賦值到對應的變量上
            console.log(sin);//log() { [native code] }
            const { log } = console;
            log('hello') // hello
            // 
            let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
            console.log(baz);//aaa
            // 嵌套賦值
            let obj = {
              p: [
                'Hello',
                { y: 'World' }
              ]
            };
            let { p,p:[x, { y }] } = obj;
            console.log(x,y,p)//Hello World p: ['Hello',{ y: 'World' }]
            //繼承賦值
            const obj1 = {};
            const obj2 = { foo: 'bar' };
            Object.setPrototypeOf(obj1, obj2);//obj1繼承obj2
            const { foo } = obj1;
            console.log(foo) // "bar"
            // 默認值
            // 錯誤的寫法
            let xx;
            // {xx} = {xx: 1};// SyntaxError: syntax error,Uncaught SyntaxError: Unexpected token '='
            ({xx} = {xx: 1});//正確寫法
            console.log(xx)
            // 古怪的,等式左邊可為空
            // ({} = [true, false]);
            // 對象可解構數組
            let arr = [1, 2, 3];
            let {0 : first, [arr.length - 1] : last} = arr;
            console.log(first,last)//1 3
        },


3、字符串的解構賦值

  • 字符串賦值
  • 類似數組的對象都有一個length屬性,因此還可以對這個屬性解構賦值
strFuc(){
            // str:'yan_yan'
            let [a,b,c,d,e,f,g] = this.str;
            console.log(a,b,c,d,e,f,g)//y a n _ y a n
            // 對數組屬性解構賦值
            let {length} = this.str;
            console.log(length)//7
        },

    

4、數值和布爾值的解構賦值

  • 解構賦值時,如果等號右邊是數值和布爾值,則會先轉為對象
  • 解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉為對象。由于undefined和null無法轉為對象,所以對它們進行解構賦值,都會報錯

let {toString: s} = 123;
console.log(s === Number.prototype.toString,s)//true ? toString() { [native code] }
let {toString: ss} = true;
console.log(ss === Boolean.prototype.toString,ss)// true ? toString() { [native code] }
// 右側必須是數組或對象,undefined和null無法轉為對象,所以對它們進行解構賦值,都會報錯
// let { prop: x } = undefined; // TypeError
// let { prop: y } = null; // TypeError


    

5、函數參數的解構賦值

  • 也可使用默認值,注意默認值是指實參的默認值而不是形參的默認值
// 函數的解構賦值可使用默認值,注意默認值是指實參的默認值而不是形參的默認值
            function move({x=1, y=1}={}) {
              return [x, y];
            }
            function move1({x, y} = { x: 0, y: 0 }) {
              return [x, y];
            }
            function move2({x, y=1} = { x: 0, y: 0 }) {
              return [x, y];
            }
            console.log(move({x: 3, y: 8})); // [3, 8]
            console.log(move({x: 3})); // [3, 1]
            console.log(move({})); // [1, 1]
            console.log(move()); // [1,1]
            console.log(move1({x: 3, y: 8})); // [3, 8]
            console.log(move1({x: 3})); // [3, 1]
            console.log(move1({})); // [undefined, 1]
            console.log(move1()); // [0,0]
            console.log(move2({x: 3, y: 8})); // [3, 8]
            console.log(move2({x: 3})); // [3, 1]
            console.log(move2({})); // [undefined, 1]
            console.log(move2()); // [0,0]

6、圓括號問題
解構賦值雖然很方便,但是解析起來并不容易。對于編譯器來說,一個式子到底是模式,還是表達式,沒有辦法從一開始就知道,必須解析到(或解析不到)等號才能知道。
由此帶來的問題是,如果模式中出現圓括號怎么處理。ES6 的規則是,只要有可能導致解構的歧義,就不得使用圓括號。
可以使用圓括號的情況只有一種:賦值語句的非模式部分,可以使用圓括號
總結:
不管是哪一類的解構賦值,等式右邊的數據必須是對象形式(數組也是一種對象形式)
————————————————
版權聲明:本文為CSDN博主「Yan_an_n」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_44258964/article/details/105643553

淺析HTTP協議

前端達人

目錄

HTTP協議

HTTP請求:

HTTP響應:

會話與會話狀態:

Cookie

Session

Cookie和Session的區別

HTTP協議


 HTTP請求:
Post /test.php HTTP/1.1                               //請求行以一個方法符號開頭,以空格分開,后面跟著請求的URI和協議的版本

Host: www.test.com                                       //請求頭

User-agent:mozilla/5.0(windows NT 6.1: rv: 15.0)

Gecko/20100101 firefox15.0

                                                                                    //空白行,代表請求頭結束

Username=admin&passwd=admin                             //請求正文

HTTP請求方法



GET       請求獲取Request-URI所標識的資源

POST     在Request-URI所標識的資源后附加新的數據

HEAD    請求獲取由Request-URI所標識的資源的響應消息報頭

PUT       請求服務器存儲一個資源,并用Request-URI作為其標識

常用的為GET和POST;GET和POST的區別:

GET提交的內容會直接顯示在URL中,私密性較差,可以用于顯示一些公共資源;但是GET效率會比較高。

POST不會將內容顯示在URL中,可以用于提交一些敏感數據,例如用戶名或密碼。

HTTP響應:
HTTP/1.1 200 OK                                         //響應行由協議版本號,響應狀態碼和文本描述組成

Data:sun,15 nov 2018 11:02:04  GMT    //響應頭

Server:bfe/1.0.8.9

……

Connection: keep-alive

                                                                      //空白行,代表響應頭結束

<html>

</html><title>index.heml</title>                  //響應正文

HTTP的狀態碼:

狀態代碼由三位數字組成,第一個數字定義了響應的類別,且有五種可能取值。

1xx:指示信息 —— 表示請求已接收,繼續處理。

2xx:成功 —— 表示請求已被成功接收、理解、接受。

3xx:重定向 —— 要完成請求必須進行更進一步的操作。

4xx:客戶端錯誤 —— 請求有語法錯誤或請求無法實現。

5xx:服務器端錯誤 —— 服務器未能實現合法的請求。

常見狀態代碼、狀態描述的說明如下。

200 OK:客戶端請求成功。

400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解。

401 Unauthorized:請求未經授權,這個狀態代碼必須和 WWW-Authenticate 報頭域一起使用。

403 Forbidden:服務器收到請求,但是拒絕提供服務。

404 Not Found:請求資源不存在,舉個例子:輸入了錯誤的URL。

500 Internal Server Error:服務器發生不可預期的錯誤。

503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間后可能恢復正常。

會話與會話狀態:
       Web中的會話是指一個客戶端瀏覽器與web服務器之間連續發生一系列請求和響應過程。會話狀態是指在會話過程中產生的狀態信息;借助會話狀態,web服務器能夠把屬于同一會話中的一系列的請求和響應關聯起來。

Cookie
概述

       Cookie是一種在客戶端保持HTTP狀態信息的技術,它好比商場發放的優惠卡。在瀏覽器訪問Web服務器的某個資源時,由Web服務器在在HTTP響應頭中附帶傳送給瀏覽器一片數據,web服務器傳送給各個客戶端瀏覽器的數據是可以各不相同的。

       一旦Web瀏覽器保存了某個Cookie,那么它在以后每次訪問該Web服務器是都應在HTTP請求頭中將這個Cookie回傳個Web服務器。Web服務器通過在HTTP響應消息中增加Set-Cookie響應頭字段將CooKie信息發送給瀏覽器,瀏覽器則通過在HTTP請求消息中增加Cookie請求頭字段將Cookie回傳給Web服務器。

       一個Cookie只能標識一種信息,它至少含有一個標識該消息的名稱(NAME)和和設置值(VALUE)。一個Web瀏覽器也可以存儲多個Web站點提供的Cookie。瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。

傳送示意圖



特點

存儲于瀏覽器頭部/傳輸與HTTP頭部,寫時帶屬性,讀時無屬性。由三元【name,domain,path】唯一確定Cookie。

Set-Cookie2響應頭字段

Set-Cookie2頭字段用于指定WEB服務器向客戶端傳送的Cookie內容,但是按照Netscape規范實現Cookie功能的WEB服務器, 使用的是Set-Cookie頭字段,兩者的語法和作用類似。Set-Cookie2頭字段中設置的cookie內容是具有一定格式的字符串,它必須以Cookie的名稱和設置值開頭,格式為"名稱=值”,后面可以加上0個或多個以分號(;) 和空格分隔的其它可選屬性,屬性格式一般為 "屬性名=值”。

除了“名稱=值”對必須位于最前面外,其他的可選屬性可以任意。Cookie的名稱只能由普通的英文ASCII字符組成,瀏覽器不用關心和理解Cookie的值部分的意義和格式,只要WEB服務器能理解值部分的意義就行。大多數現有的WEB服務器都是采用某種編碼方式將值部分的內容編碼成可打印的ASCII字符,RFC 2965規范中沒有明確限定編碼方式。

舉例:   Set-Cookie2: user-hello; Version=1; Path=/

Cookie請求頭字段

Cookie請求頭字段中的每個Cookie之間用逗號(,)或分號(;)分隔。在Cookie請求字段中除了必須有“名稱=值”的設置外,還可以有Version、path、domain、port等屬性;在Version、path、domain、port等屬性名之前,都要增加一個“$”字符作為前綴。Version屬性只能出現一次,且要位于Cookie請求頭字段設置值的最前面,如果需要設置某個Cookie信息的Path、Domain、Port等屬性,它們必須位于該Cookie信息的“名稱=值”設置之后。

       瀏覽器使用Cookie請求頭字段將Cookie信息會送給Web服務器;多個Cookie信息通過一個Cookie請求頭字段會送給Web服務器。

瀏覽器會根據下面幾個規則決定是否發送某個Cookie信息:

       1、請求主機名是否與某個存儲的Cookie的Domain屬性匹配

       2、請求的端口號是否在該Cookie的Port屬性列表中

       3、請求的資源路徑是否在該Cookie的Path屬性指定的目錄及子目錄中

       4、該Cookie的有效期是否已過

Path屬性的指向子目錄的Cookie排在Path屬性指向父目錄的Cookie之前

舉例: Cookie: $Version=1; Course=Java; $Path=/hello/lesson;Course=vc; $Path=/hello

Cookie的安全屬性

secure屬性

當設置為true時,表示創建的Cookie會被以安全的形式向服務器傳輸,也就是只能在HTTPS連接中被瀏覽器傳遞到服務器端進行會話驗證,如果是HTTP連接則不會傳遞該信息,所以不會被竊取到Cookie的具體內容。

 HttpOnly屬性

如果在Cookie中設置了"HttpOnly"屬性,那么通過程序(JS腳本、Applet等)將無法讀取到Cookie信息,這樣能有效的防止XSS攻擊。

總結:secure屬性 是防止信息在傳遞的過程中被監聽捕獲后信息泄漏,HttpOnly屬性的目的是防止程序獲取cookie后進行攻擊這兩個屬性并不能解決cookie在本機出現的信息泄漏的問題(FireFox的插件FireBug能直接看到cookie的相關信息)。

Session
使用Cookie和附加URL參數都可以將上一-次請求的狀態信息傳遞到下一次請求中,但是如果傳遞的狀態信息較多,將極大降低網絡傳輸效率和增大服務器端程序處理的難度。

概述

Session技術是一種將會話狀態保存在服務器端的技術,它可以比喻成是醫院發放給病人的病歷卡和醫院為每個病人保留的病歷檔案的結合方式??蛻舳诵枰邮?、記憶和回送Session的會話標識號,Session可以且通常是借助Cookie來傳遞會話標識號。



Session的跟蹤機制

HttpSession對象是保持會話狀態信息的存儲結構,一個客戶端在WEB服務器端對應一個各自的HttpSession對象。WEB服務器并不會在客戶端開始訪問它時就創建HttpSession對象,只有客戶端訪問某個能與客戶端開啟會話的服務端程序時,WEB應用程序才會創建一個與該客戶端對應的HttpSession對象。WEB服務器為HttpSession對象分配一個獨一無的會話標識號, 然后在響應消息中將這個會話標識號傳遞給客戶端??蛻舳诵枰涀挊俗R號,并在后續的每次訪問請求中都把這個會話標識號傳送給WEB服務器,WEB服務器端程序依據回傳的會話標識號就知道這次請求是哪個客戶端發出的,從而選擇與之對應的HttpSession對象。

WEB應用程序創建了與某個客戶端對應的HttpSession對象后,只要沒有超出一個限定的空閑時間段,HttpSession對象就駐留在WEB服務器內存之中,該客戶端此后訪問任意的Servlet程序時,它們都使用與客戶端對應的那個已存在的HttpSession對象。

Session是實現網上商城的購物車的最佳方案,存儲在某個客戶Session中的一個集合對象就可充當該客戶的一個購物車。

超時管理

WEB服務器無法判斷當前的客戶端瀏覽器是否還會繼續訪問,也無法檢測客戶端瀏覽器是否關閉,所以,即使客戶已經離開或關閉了瀏覽器,WEB服務器還要保留與之對應的HttpSession對象。隨著時間的推移而不斷增加新的訪問客戶端,WEB服務器內存中將會因此積累起大量的不再被使用的HttpSession對象,并將最終導致服務器內存耗盡。WEB服務器采用“超時限制”的辦法來判斷客戶端是否還在繼續訪問如果某個客戶端在一定的時間之 內沒有發出后續請求,WEB服務器則認為客戶端已經停止了活動,結束與該客戶端的會話并將與之對應的HttpSession對象變成垃圾。

如果客戶端瀏覽器超時后再次發出訪問請求,Web服務器則認為這是一個新的會話開始,將為之創建新的Httpsession對象和分配新的會話標識號。

利用Cookie實現Session的跟蹤

如果WEB服務器處理某個訪問請求時創建了新的HttpSession對象,它將把會話標識號作為一個Cookie項加入到響應消息中,通常情況下,瀏覽器在隨后發出的訪問請求中又將會話標識號以Cookie的形式回傳給WEB服務器。WEB服務器端程序依據回傳的會話標識號就知道以前已經為該客戶端創建了HttpSession對象,不必再為該客戶端創建新的HttpSession對象,而是直接使用與該會話標識號匹配的HttpSession對象,通過這種方式就實現了對同一個客戶端的會話狀態的跟蹤。

利用URL重寫實現Session跟蹤

Servlet規范中引入了一種補充的會話管理機制,它允許不支持Cookie的瀏覽器也可以與WEB服務器保持連續的會話。這種補充機制要求在響應消息的實體內容中必須包含下一 次請求的超鏈接,并將會話標識號作為超鏈接的URL地址的一個特殊參數。將會話標識號以參數形式附加在超鏈接的URL地址后面的技術稱為URL重寫。 如果在瀏覽器不支持Cookie或者關閉了Cookie功能的情況下,WEB服務器還要能夠與瀏覽器實現有狀態的會話,就必須對所有能被客戶端訪問的請求路徑(包括超鏈接、form表單的action屬性設置和重定向的URL)進行URL重寫。

Cookie和Session的區別
session和cookies同樣都是針對單獨用戶的變量(或者說是對象好像更合適點),不同的用戶在訪問網站的時候都會擁有各自的session或者cookies,不同用戶之間互不干擾。

他們的不同點是:

1,存儲位置不同

session在服務器端存儲,比較安全,但是如果session較多則會影響性能

cookies在客戶端存儲,存在較大的安全隱患

2,生命周期不同

session生命周期在指定的時間(如20分鐘) 到了之后會結束,不到指定的時間,也會隨著瀏覽器進程的結束而結束。

cookies默認情況下也隨著瀏覽器進程結束而結束,但如果手動指定時間,則不受瀏覽器進程結束的影響。

總結:簡而言之,兩者都是保存了用戶操作的歷史信息,但是存在的地方不同;而且session和cookie的目的相同,都是為了克服HTTP協議無狀態的缺陷,但是完成方法不同。Session通過cookie在客戶端保存session id,將用戶的其他會話消息保存在服務端的session對象中;而cookie需要將所有信息都保存在客戶端,因此存在著一定的安全隱患,例如本地Cookie中可能保存著用戶名和密碼,容易泄露。
————————————————
版權聲明:本文為CSDN博主「悲觀的樂觀主義者」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_43997530/article/details/105650267


日歷

鏈接

個人資料

藍藍設計的小編 http://m.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
亚洲欧美激情四射在线日| 亚洲成人黄色在线| 国产欧美一区二区精品久导航| 99一区二区三区| 日韩欧美一区二区三区久久| 免费国产自久久久久三四区久久| 精品视频在线一区二区| 日韩精品一区二区三区| caoporen国产精品| 91福利在线导航| 欧美美女一区二区在线观看| h视频在线免费| 精品国产欧美日韩一区二区三区| 91精品国产91久久综合桃花| 国产精品最新在线观看| 日本10禁啪啪无遮挡免费一区二区| 亚洲乱码一区二区三区三上悠亚| 成人在线视频网址| 国产女主播在线一区二区| 亚洲黄色毛片| 日韩风俗一区 二区| 久久国产精品久久国产精品| 在线看片福利| 欧美日韩中文字幕一区| 日本精品免费观看高清观看| 中文字幕亚洲一区在线观看| 欧美日韩激情电影| 亚洲人成77777在线观看网| 亚洲小说欧美另类社区| 精品国产老师黑色丝袜高跟鞋| 精品久久久久久久久久久久久| 麻豆视频在线播放| 国产成人精品自拍| 4438x成人网全国最大| 尤物99国产成人精品视频| 午夜在线精品偷拍| 视频在线这里都是精品| 欧美精品一区二区视频| 成人小视频免费观看| 久草综合在线| 久久久久av| 久久久影视传媒| 九九热这里有精品| 亚洲午夜久久久影院| 国产精品15p| 蜜桃视频一区| 综合久久2o19| 精品少妇一区二区30p| 日韩av电影免费观看| 久久婷婷色综合| 欧美日韩另类视频| 欧美精品aa| 国产一区在线观| 国产精品人人做人人爽| 国产日本欧美在线观看| 国产精品久久久久久久久久久久冷| 羞羞色国产精品| 91精品国产高清自在线看超| www.日韩| 亚洲成人中文| 日韩色视频在线观看| 国产高清不卡一区二区| 国内精品一区二区三区四区| 国产在线天堂www网在线观看| 一区二区三区中文免费| 国产精品三级在线观看| 风间由美性色一区二区三区| 99国产精品久久久久久久久久久| 日韩一区二区在线免费| 91在线一区二区三区| 午夜精品aaa| 96视频在线观看欧美| 国产精品1区2区3区在线观看| 久久视频免费观看| 一本大道久久a久久精二百| 日本不卡视频在线| 欧美大胆性生话| 亚洲香蕉av在线一区二区三区| 精品一区二区三区中文字幕视频| aa国产精品| 欧美黄色片视频| 久久久777精品电影网影网| 国产视频一区不卡| 久久精品人人做人人爽电影蜜月| 秋霞午夜鲁丝一区二区老狼| 日本成人网址| 欧美三级在线视频| 国产乱色国产精品免费视频| 欧美成人vr18sexvr| 国产精成人品2018| 国产精品久久久久久影院8一贰佰| 好吊妞www.84com只有这里才有精品| 在线观看av免费| 精品久久人人做人人爰| 精品国产美女在线| 欧美日韩国产成人在线观看| 97香蕉超级碰碰久久免费的优势| 麻豆精品视频在线观看| 精品久久香蕉国产线看观看亚洲| 97超级碰碰人国产在线观看| 久久精品人人做人人爽97| 777精品视频| 日韩你懂的在线观看| 欧美大片第1页| 欧美丝袜美女中出在线| 久久精品欧美一区二区三区麻豆| 羞羞的网站在线观看| 一区二区三区在线高清| heyzo一本久久综合| 日韩av网站电影| 日韩欧美成人一区二区| 国产日韩精品视频| 久久精品中文字幕免费mv| 欧美男人的天堂| 欧美精品免费在线| 在线影视一区二区三区| 国产精品久久久久久久久久小说| 国产精品初高中精品久久| 亚洲护士老师的毛茸茸最新章节| 日韩欧美成人激情| 国产一区二区三区在线播放免费观看| 欧美黑人xxxⅹ高潮交| 欧美理论电影在线播放| 国产精品白丝jk白祙喷水网站| 蜜桃视频在线观看免费视频| 欧美日韩国产999| 91av网站在线播放| 天堂成人免费av电影一区| 亚洲精蜜桃久在线| 久久综合久久综合久久| 日本亚州欧洲精品不卡| 欧美激情xxxx性bbbb| 天堂√在线观看一区二区| 91成人午夜| 中文字幕色婷婷在线视频| 欧美国产一区二区在线| 午夜在线视频观看日韩17c| 欧美体内she精视频在线观看| 一本久道久久综合狠狠爱| 欧美视频在线看| 午夜精品国产更新| 欧美va亚洲va国产综合| 国产69精品久久| 91国产精品视频在线| 国产激情小视频在线| 国产精品宾馆| 国产福利一区二区三区| 超碰精品一区二区三区乱码| 欧美激情在线一区| 精品一区二区三区的国产在线观看| 精品国产污污免费网站入口| 成人精品一区二区三区校园激情| 九九热精品视频| zzjj国产精品一区二区| 精品国产一二三区| 欧美成年人视频网站| 亚洲成人一区在线观看| 波多野结衣一区二区三区| 一级毛片视频在线| 日韩免费看片| 国产成人精品亚洲777人妖| 精品国产三区在线| 亚洲精品电影网在线观看| 国产大片一区二区|