<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        spoonysnail

        spoonysnail 查看完整檔案

        杭州編輯  |  填寫畢業院校  |  填寫所在公司/組織填寫個人主網站
        編輯
        _ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 個人簡介什么都沒有

        個人動態

        spoonysnail 關注了標簽 · 3月9日

        java

        Java 是一種可以撰寫跨平臺應用軟件的面向對象的程序設計語言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序設計語言和 Java 平臺(即 JavaSE, JavaEE, JavaME)的總稱。Java 技術具有卓越的通用性、高效性、平臺移植性和安全性。

        Java編程語言的風格十分接近 C++ 語言。繼承了 C++ 語言面向對象技術的核心,Java舍棄了 C++ 語言中容易引起錯誤的指針,改以引用取代,同時卸載原 C++ 與原來運算符重載,也卸載多重繼承特性,改用接口取代,增加垃圾回收器功能。在 Java SE 1.5 版本中引入了泛型編程、類型安全的枚舉、不定長參數和自動裝/拆箱特性。太陽微系統對 Java 語言的解釋是:“Java編程語言是個簡單、面向對象、分布式、解釋性、健壯、安全與系統無關、可移植、高性能、多線程和動態的語言”。

        版本歷史

        重要版本號版本代號發布日期
        JDK 1.01996 年 1 月 23 日
        JDK 1.11997 年 2 月 19 日
        J2SE 1.2Playground1998 年 12 月 8 日
        J2SE 1.3Kestrel2000 年 5 月 8 日
        J2SE 1.4Merlin2002 年 2 月 6 日
        J2SE 5.0 (1.5.0)Tiger2004 年 9 月 30 日
        Java SE 6Mustang2006 年 11 月 11 日
        Java SE 7Dolphin2011 年 7 月 28 日
        Java SE 8JSR 3372014 年 3 月 18 日
        最新發布的穩定版本:
        Java Standard Edition 8 Update 11 (1.8.0_11) - (July 15, 2014)
        Java Standard Edition 7 Update 65 (1.7.0_65) - (July 15, 2014)

        更詳細的版本更新查看 J2SE Code NamesJava version history 維基頁面。

        新手幫助

        不知道如何開始寫你的第一個 Java 程序?查看 Oracle 的 Java 上手文檔。

        在你遇到問題提問之前,可以先在站內搜索一下關鍵詞,看是否已經存在你想提問的內容。

        命名規范

        Java 程序應遵循以下的 命名規則,以增加可讀性,同時降低偶然誤差的概率。遵循這些命名規范,可以讓別人更容易理解你的代碼。

        • 類型名(類,接口,枚舉等)應以大寫字母開始,同時大寫化后續每個單詞的首字母。例如:String,ThreadLocal,and NullPointerException。這就是著名的帕斯卡命名法。
        • 方法名 應該是駝峰式,即以小寫字母開頭,同時大寫化后續每個單詞的首字母。例如:indexOf,printStackTrace,interrupt。
        • 字段名 同樣是駝峰式,和方法名一樣。
        • 常量表達式的名稱static final 不可變對象)應該全大寫,同時用下劃線分隔每個單詞。例如:YELLOW,DO_NOTHING_ON_CLOSE。這個規范也適用于一個枚舉類的值。然而,static final 引用的非不可變對象應該是駝峰式。

        Hello World

        public class HelloWorld {
            public static void main(String[] args) {
                System.out.println("Hello, World!");
            }
        }

        編譯并調用:

        javac -d . HelloWorld.java
        java -cp . HelloWorld

        Java 的源代碼會被編譯成可被 Java 命令執行的中間形式(用于 Java 虛擬機的字節代碼指令)。

        可用的 IDE

        學習資源

        常見的問題

        下面是一些 SegmentFault 上在 Java 方面經常被人問到的問題:

        (待補充)

        關注 141262

        spoonysnail 發布了文章 · 2020-08-07

        微前端:SingleSpa實踐小結

        背景:

        此前接手了一個老項目(3、4年前開始的一個項目),面臨的一個最大的問題是老舊代碼的維護和迭代。維護老舊代碼的可選方式并不多:

        1. 推倒重來,全部進行重構 => 工作量繁重,成本極高
        2. 依照舊有技術選型進行維護和迭代 => emmmm,一言難盡
        3. 對于迭代需求采用新的技術架構和選型,與已有功能解耦,盡量減少對舊代碼的依賴和舊邏輯的修改

        出于工作量和項目后續的可維護性和迭代性相對而言第3種方案比較可行。因此調研了一下微前端方案來進行改造。

        調研:

        微前端入門
        每日優鮮微前端改造

        Alibaba-console-os VS Single-Spa

        Alibaba-console-os

        接入方法

        1. main.js 導出Vue做如下改造
        import App from './App.vue'  
        import { mount } from '@alicloud/console-os-vue-portal'  
        export default mount({  
            el: '#app',  
            render: h => h(App)  
        })  
        1. vue.config.js 做如下配置,增加 consoleOs插件,并設定相應id
        module.exports = {  
            publicPath: 'http://localhost:8080/',  
            "pluginOptions": {  
                "consoleOs": {  
                    "id": "os-example-vue"  
                }  
            }  
        } 
        1. 在 Host App中通過manifest.json文件引入相應子應用
        import { start } from "@alicloud/console-os-kernal";  
        import Application from "@alicloud/console-os-react-app";  
        function App() {  
            return ( 
                <div className="App">
                    <div className="vue"> 
                        <Application
                            id="os-example-vue"
                            manifest="http://localhost:8080/os-example-vue.manifest.json"/>
                    </div>
                </div>  );  
        }  
        Single-Spa

        Ref: https://www.jianshu.com/p/c0f4b837dbea
        Demo

        接入方法

        1. main.js vue導出文件做如下改造
        import Vue from 'vue';  
        import singleSpaVue from 'single-spa-vue';  
        import Hello from './main.vue'  
        const vueLifecycles = singleSpaVue({  
            Vue,  
            appOptions: {  
                el: '#vue',  
                render: r => r(Hello)  
            }  
        });  
        export const bootstrap = [  
            vueLifecycles.bootstrap,  
        ];  
        export const mount = [  
            vueLifecycles.mount,  
        ];  
        export const unmount = [  
            vueLifecycles.unmount,  
        ];
        1. single-spa.config.js 注冊應用
        import { registerApplication, start } from 'single-spa'  
        registerApplication(  
            'vue',  
            () => import('./src/vue/main.js'),  
            () => location.pathname === "/react" ? false : true
        );  
        start();
        1. index.html 配置
        <html>  
            <head></head>  
            <body>  
                <div id="react"></div>  
                <div id="vue"></div>  
                <script data-original="/dist/single-spa.config.js"></script>  
            </body>  
        </html> 
        1. webpack配置
        module.exports = {  
            entry: {  
                'single-spa.config': './single-spa.config.js',  
            },  
        }
        Alibaba-console-osSingleSpa
        子應用技術棧React、Vue、AngularReact、Vue、Angular
        主應用技術棧React、Angular不限
        子應用資源配置模式manifest.jsonsingle-spa.config.js

        選擇SingleSpa的原因

        1. Alibaba-console-os 的Host App不支持Vue,我們團隊的主技術棧是Vue
        2. Alibaba-console-os子應用資源配置通過manifest.json引入,不適用我們公司的資源部署方式

        Single-Spa實踐:

        架構

        1. 加載器:用來調度子應用,決定何時展示哪個子應用。
        2. 包裝器:對現有的應用包裝,使得加載器可以使用它們
        3. 主應用:一般是包含所有子應用公共部分的項目,
        4. 子應用:各個展示在主應用內容區的應用

        大概是這樣一個流程:用戶訪問頁面,瀏覽器運行加載器的js文件,加載器去獲取配置文件,并注冊配置文件中的各個子應用,首先加載主應用(比如菜單導航等),然后通過路由判定去動態加載子應用。

        接入流程

        1. 創建子應用,將admin工程拆分為edu-nav(菜單導航工程)、-old(原有業務功能工程)、edu-mis(新業務功能工程)
        2. 改造子應用,通過singleSpa對子應用進行包裝。需要引入single-spa-vue以及systemjs-webpack-interop。

          • single-spa-vue是針對vue項目的包裝器,包導出4個生命周期函數:bootstrap、mount、unmount、update。
          • systemjs-webpack-interop可以使webpack和systemjs一起正常工作

        image

        1. 創建主應用,充當加載器的功能,有以下幾個任務。

          1. 配置資源map,用于后續資源SystemJs引入
            image
          2. 通過SystemJs引入資源文件
            image
          3. 掛載Publisher,用于子應用之間的通信。
          4. registerApplication注冊子應用,參數依次為:注冊的子應用名稱、子應用入口文件(需要是一個函數)、控制應用是否激活的函數。
          5. singleSpa.start。啟動函數。

        經過上述步驟,整個工程singleSpa改造完成。

        實踐中發現的一些問題

        1. 需要考慮隔離js,避免css沖突,并考慮按需加載資源
        2. 公用數據信息的同步:菜單或業務線的切換發生時會通過Publisher進行通知,但是在切換業務線后再從子應用A切換到子應用B時會出現子應用B未曾接收到Publisher業務線切換消息,導致業務線不準確。因此,需要在菜單工程中特殊處理,對路由進行攔截判斷,并再次進行消息通知。
        3. 要格外注意在原型上掛載一些全局變量或全局函數的行為,容易被后者覆蓋。
        查看原文

        贊 0 收藏 0 評論 0

        spoonysnail 贊了回答 · 2020-02-28

        解決axios 參數如何將[]非法字符進行轉換?

        引入qs去處理get的params, 注意table先用JSON.stringify處理

            const qs = require('qs')
            let param = {
              table: JSON.stringify([{
                key: 'OGR_FID',
                logic: '=',
                value: 1
              }, {
                key: 'OGR_FIDddddd',
                logic: '=',
                value: 2
              }])
            }
            this.$http.get('/****', {
              params: param,
              paramsSerializer: function (params) {
                return qs.stringify(params, {arrayFormat: 'repeat'})
              }
            }).then((res) => {
            })

        關注 5 回答 5

        spoonysnail 回答了問題 · 2020-02-10

        解決mongodb 不同field的sub-document元素求和

        可以用mapReduce做,map里用固定值作為key,并計算單個人十個時間點的總和作為value;reduce里再進行所有人的求和。

        db.collection.mapReduce(function(){
            const hourDict = this.data.hour;
            const personSum = hourDict.0.num + hourDict.1.num + ...;
            emit('allHours', personSum);
        }, function(key, values){
            return Array.sum(values)
        }, {
            out: 'total'    
        })

        關注 3 回答 2

        spoonysnail 收藏了文章 · 2019-12-23

        VS Code插件開發介紹(一)

        • 前言

        前段時間做了一個基于命令行的效率工具,可以自動生成組件的模板代碼。自己用起來還覺得挺好,但在組內案例幾次后大家都不愿意用,究其原因還是命令行工具使用起來門檻有點高,不方便。由于組內已經統一使用VS Code進行開發了,于是決定研究下VS Code的插件開發,讓效率工具更方便的用起來。

        • 準備工作

        為了降低開發門檻,微軟做了一個Yeoman代碼生成命令,可以很方便的生成插件開發需要的模板代碼,可以通過以下命令安裝:

        // 安裝
        npm install -g yo generator-code
        
        // 使用
        yo code

        運行完以上命令后會出現下面的操作界面,填入需要的信息即可。
        clipboard.png

        • 運行示例代碼

        代碼生成后,可以按F5開始調試插件,此時VS Code會新建一個實例并進入插件開發模式,開發中的插件可以在這個新的實例中使用。模版代碼會生成一個名為Hello World的命令,按下??P調出命令輸入窗口,然后輸入'Hello World'運行命令。如果找不到命令,重啟下VS Code再重新運行。
        clipboard.png

        • 修改代碼

        插件的入口代碼在extension.js這個文件中,主要是修改activate函數:

        export function activate(context) {
        
            // Use the console to output diagnostic information (console.log) and errors (console.error)
            // This line of code will only be executed once when your extension is activated
            console.log('Congratulations, your extension "my-first-extension" is now active!');
        
            // The command has been defined in the package.json file
            // Now provide the implementation of the command with  registerCommand
            // The commandId parameter must match the command field in package.json
            let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
                // The code you place here will be executed every time your command is executed
        
                // Display a message box to the user
                vscode.window.showInformationMessage('Hello World!');
            });
        
            context.subscriptions.push(disposable);
        }

        我插件的功能是用戶通過右鍵點擊導航欄,獲取選中文件夾的絕對路徑,然后提示用戶輸入新組件的名字,然后自動幫用戶生成組件的模板代碼。
        clipboard.png

        clipboard.png

        開發的關鍵點是如何獲取文件夾絕對路徑和獲取用戶輸入的組件名。尤其是獲取路徑,找了很久才找到,官網的文檔只字未提。先看代碼:

        function activate(context) {
            console.log('Congratulations, your extension "react-template" is now active!');
        
            // 注冊一個名為createFunctionalComponent的命令
            const fc = vscode.commands.registerCommand('extension.createFunctionalComponent', function (param) {
                // 文件夾絕對路徑
                const folderPath = param.fsPath;
        
                const options = {
                    prompt: "請輸入組件名: ",
                    placeHolder: "組件名"
                }
                
                // 調出系統輸入框獲取組件名
                vscode.window.showInputBox(options).then(value => {
                    if (!value) return;
        
                    const componentName = value;
                    const fullPath = `${folderPath}/${componentName}`;
        
                    // 生成模板代碼,不是本文的重點,先忽略
                    generateComponent(componentName, fullPath, ComponentType.FUNCTIONAL_COMP);
                });
            });
            
            context.subscriptions.push(pc);
        }

        代碼很簡單,就不做講解了。為了顯示Create Functional Component這個菜單項,我們需要修改package.json文件的contributes字段。activationEvents字段也要相應的改下。同時為了給插件配一個圖標,要加一個icon字段:

            "icon": "images/icon.png",
            "activationEvents": [
                "onCommand:extension.createFunctionalComponent"
            ],
            "contributes": {
                "commands": [
                    {
                        "command": "extension.createFunctionalComponent",
                        "title": "Create Functional Component"
                    }
                ],
                "menus": {
                    "explorer/context": [
                        {
                            "command": "extension.createFunctionalComponent",
                            "group": "1_modification"
                        }
                    ]
                }
            },

        詳細的contributes配置可以看這里。配置好menus之后,registerCommand的第二個函數參數就能取到文件或文件夾的絕對路徑了。

        • 發布插件

        插件開發完后就可以發布了,需要安裝vsce

        npm install -g vsce

        安裝完后,需要去Azure DevOps注冊并生成一個access token。詳細的流程可以看這里。生成toke的時候有兩個地方需要注意下:
        clipboard.png

        token生成后就可以登錄和發布了。如果有任何的修改,要注意修改package.json里面版本號才能發布成功。發布成功后,很快就能在VS Code的插件市場搜到了。
        clipboard.png

        • 總結

        本文介紹了VS Code插件開發的基本流程,實現了一個簡單的插件。本文只涉及到VS Code插件系統的冰山一角,更多的高級功能以后接觸到的時候再作介紹。如果想再作進一步的了解,可以從這里開始。更多的 VS Code 開發技巧,可以看這個系列的第二篇:VS Code插件開發介紹(二)

        查看原文

        spoonysnail 收藏了文章 · 2019-12-23

        如何處理 Web 圖片優化?

        未優化的圖片是影響網站性能的主要因素之一,尤其會影響初次加載。取決于圖像的分辨率和畫質,圖片可能占據整個網站流量的 70%.

        生產環境出現未優化的圖片并顯著影響初次加載速度的現象還是挺常見的。缺乏經驗的開發者通常沒有意識到這一潛在問題,也不了解各種優化圖片的工具和方法。

        本文的目標是介紹優化 web 圖片的主要工具和方法。

        計算 JPG 文件尺寸

        未壓縮圖片的尺寸很容易計算,只需將圖片的長寬相乘(px 值),再乘以 3 字節(因為 RGB 色彩系統使用 24 個位元)。所得結果除以 1,048,576(1024 * 1024)即得到兆字節。

        image_size?=?(image_width?*?image_height?*?3)?/?1048576

        比如,計算分辨率為 1366px x 768px 的未壓縮圖片的大?。?/p>

        1366?*?768?*?3?/?1048576?=?3Mb

        現在網站的尺寸平均在 2Mb 和 3Mb 之間,想象一下,一張未壓縮的圖片就占掉了 80% 的流量。在網速較慢的移動網絡上,3Mb 大小的圖片要花很久才能加載完畢。如果等待網站加載的用戶大部分時間花在等待單張圖片加載,那網站會損失不少流量。想想就可怕,是嗎?

        所以,在保證圖片分辨率和畫質可接受的前提下,我們可以做什么來優化下圖片呢?

        在線圖片優化

        如果你的項目是一個簡單的靜態網站,只有少量不經常變動(甚至從來不會變動)的圖片,那么你可以直接使用在線工具。這些工具使用各種算法壓縮圖像,效果很不錯,對簡單項目而言完全夠用。

        就我個人所知,比較著名的在線工具有:

        • Compressor.io,支持 JPG、PNG、SVG、GIF,每次上傳 1 個文件
        • Squoosh,支持 JPG、PNG、SVG、GIF,每次上傳 1 個文件
        • Optimizilla,支持 JPG、PNG,最多每次上傳 20 個文件
        • TinyPNG,支持 JPG、PNG,最多每次上傳 20 個文件
        • SVGMinify,支持 SVG,每次上傳 1 個文件
        • svgomg,支持 SVG,每次上傳 1 個文件

        自動化解決方案

        然而,如果你做的是多人協作的復雜項目,使用大量圖片,在加入每張圖片時都手動操作一下很乏味。同時,還存在由于人為錯誤或其他因素導致一些圖片沒有優化的風險。

        復雜項目常常使用同樣復雜的構建系統,比如 Gulp、Webpack、Parcel。配置一下這類構建系統,加入圖片優化插件很方便。這樣就可以完全自動化圖片優化過程,在項目中加入圖片后就可以優化它們。

        就我所知,最有名的插件是 imagemin,可以作為命令行工具使用,也可以作為構建工具的插件使用:

        圖片加載優化

        我們前面介紹了如何通過壓縮圖片降低文件尺寸,但不過多改變圖片分辨率和影響畫質。盡管優化圖片后文件尺寸能降低不少,但一次性加載大量優化過的圖片(比如電商網站的商品列表頁面)還是會影響性能。

        懶加載

        懶加載也叫按需加載,意思是僅加載當前視圖(用戶屏幕顯示范圍)內的圖片,不加載其他圖片(直到它們出現在當前視圖內時才加載)。

        只有較新版本的瀏覽器才支持原生的懶加載特性,不過有許多基于 JavaScript 的方案。

        • 原生懶加載
        <img?data-original="image.jpg"?loading="lazy"?alt="Sample?image"?/>
        • 基于 JavaScript 的方案

        就我所知,最知名的方案有:

        verlok/lazyload
        yall.js
        Blazy (現在沒有維護)

        漸進式圖片

        盡管懶加載在性能方面表現出色,但是用戶滾動屏幕后需要盯著空白區域等待圖片加載,這樣的用戶體驗不太好。網速慢的情況下,下載圖片會非常慢。所以我們還需要漸進式圖片。

        漸進式圖片的意思是在高畫質圖像加載完之前會先顯示低畫質版本。低畫質版本由于畫質低、壓縮率高,尺寸很小,加載很快。在兩者之間我們也可以根據需要顯示不同畫質的版本。

        類似于先加載頁面的骨架,漸進式圖片這一技術讓用戶產生圖片加載變快的印象。用戶不再盯著一片空白區域等待事情發生,而能看到圖像變得越來越清晰。
        漸進式圖片有基于 JavaScript 實現的方案:
        progressive-image。

        響應式圖片

        我們還需要留意使用尺寸合適的圖片。

        例如,假設圖片在桌面瀏覽器上顯示的最大寬度為 1920px,平板上的最大寬度為 1024px,手機上的最大寬度為 568px,那么最簡單的方案是使用 1920px 的圖片,這樣可以滿足所有場景。不過,這種情況下,網速慢、網絡不穩定的智能手機用戶需要等很久圖片才能加載完畢,這就又碰到了我們文章開頭提到的問題。

        好在我們可以通過 picture 元素告訴瀏覽器基于媒體查詢下載相應的圖片。盡管現在 93% 的用戶使用的瀏覽器都支持這一特性,但是這個元素內部還是包含了一個 img 元素,以兼容不支持這一特性的瀏覽器。

        <picture>??<source?media="(min-width:?1025px)"?srcset="image_desktop.jpg">??<source?media="(min-width:?769px)"?srcset="image_tablet.jpg">??<img?data-original="image_mobile.jpg"?alt="Sample?image"></picture>

        使用 CDN

        Cloudinary、Cloudflare 之類的 CDN 服務可以在服務器上優化圖片,將優化后的圖片傳送給用戶。如果你的站點使用 CDN,可以看下靜態資源優化選項。這樣我們就不用操心圖片優化,由 CDN 在服務端完成優化。我們只需要操心懶加載、漸進式圖片等前端的加載方案。

        WebP 圖像格式

        WebP 是由 Google 開發的專為 web 優化的圖像格式。根據 canIUse 的數據,大部分用戶使用的瀏覽器支持 WebP 格式。另外使用 picture 元素也可以很方便地兼容不支持 WebP 的瀏覽器。

        <picture>??<source?type="image/webp"?srcset="image.webp"?/>??<source?srcset="image.jpg"?/>??<img?data-original="image.jpg"?alt="Sample?image"?/></picture>

        有很多在線文件格式轉換工具可以把圖片轉為 WebP 格式,不過 CDN 服務可以在服務端完成這一格式轉化。

        為高分屏優化

        考慮高分屏很有必要,不過這個更多的是用戶體驗優化。

        例如,假定我們在 768px 的屏幕上顯示一張 768px x 320px 的圖片。但是屏幕有 2x 的密度,也就是說屏幕寬度實際是 2 x 768 = 1536 px。這就意味著我們將 768 px 的圖片拉升到 1536 px,這就導致高分屏上的圖片看起來很模糊。

        為了解決這一問題,我們需要提供為高分屏優化的圖片。我們需要單獨創建相當于普通屏幕 2 倍或 3 倍分辨率的圖片,然后在 srcset 屬性上使用 2x 標簽表明這是為高分屏準備的圖片。

        <img?data-original="image-1x.jpg"?srcset="image-2x.jpg?2x"?alt="Sample?image"?/>

        例子

        支持高分屏的響應式 WebP/PNG 圖片:

        <picture>????<source?srcset="./images/webp/hero-image-420-min.webp?1x,?./images/webp/hero-image-760-min.webp?2x"?type="image/webp"?media="(max-width:?440px)">????<source?srcset="./images/minified/hero-image-420-min.png?1x,?./images/minified/hero-image-760-min.png?2x"?media="(max-width:?440px)">????<source?srcset="./images/webp/hero-image-550-min.webp?1x,?./images/webp/hero-image-960-min.webp?2x"?type="image/webp"?media="(max-width:?767px)">????<source?srcset="./images/minified/hero-image-550-min.png?1x,?./images/minified/hero-image-960-min.png?2x"?media="(max-width:?767px)">????<source?srcset="./images/webp/hero-image-420-min.webp?1x,?./images/webp/hero-image-760-min.webp?2x"?type="image/webp"?media="(max-width:?1023px)">????<source?srcset="./images/minified/hero-image-420-min.png?1x,?./images/minified/hero-image-760-min.png?2x"?media="(max-width:?1023px)">????<source?srcset="./images/webp/hero-image-760-min.webp?1x,?./images/webp/hero-image-960-min.webp?2x"?type="image/webp"?media="(max-width:?1919px)">????<source?srcset="./images/minified/hero-image-760-min.png?1x,?./images/minified/hero-image-960-min.png?2x"?media="(max-width:?1919px)">????<source?srcset="./images/webp/hero-image-960-min.webp"?type="image/webp">????<source?srcset="./images/minified/hero-image-960-min.png">????<img??data-original="./images/minified/hero-image-960-min.png"?alt="Example"></picture>

        結語 —— 優化優先級

        1. 使用優化后的圖片(使用自動構建工具、在線服務、CDN 優化)
        2. 使用懶加載(在瀏覽器有更好的原生支持前考慮使用 JS 方案)
        3. 為高分屏優化圖片
        4. 使用 WebP 格式
        5. 使用漸進式圖片

        可選: 如果條件允許,記得使用 CDN 加速圖片(和其他靜態資源)。

        內容經授權轉載自 New Frontend 網站。

        查看原文

        spoonysnail 收藏了文章 · 2019-12-23

        我是如何從零學習開發一款跨平臺桌面軟件的(Markdown編輯器)

        作者:薛勤,互聯網從業者,編程愛好者。

        本文首發自公眾號:代碼藝術(ID:onblog)未經許可,不可轉載

        原始沖動

        最近一直在學習 Electron 開發桌面應用程序,目的是想做一個桌面編輯器,雖然一直在使用Typore這款神器,但無奈Typore太過國際化,在國內水土不服,無法滿足我的一些需求。

        比如實現本地圖片上傳到云端(mac版可以借助iPic),無法幫我把本地圖片和文章一起發布到博客園、CSDN、SegmentFault、掘金等國內知名博客平臺,要么使用一些免費或付費的圖床,借助類似iPic的工具,把圖片一鍵上傳到云端。

        我個人也嘗試過七牛云的免費10G存儲空間,但是說實話,這些免費的空間到最后一定是為了讓你成為付費用戶,各種限制各種吐槽在網上很容易可以搜索到。

        免費的圖床如新浪微博等,還算是比較好的圖床工具,相比一些網絡上的壓根不知道啥公司甚至是歸屬個人的免費圖床,新浪應該是比較靠譜的,相對來說可以保證圖片的存活時間,我個人用過一些免費的圖床網站,記得印象深刻的就是服務器出問題,網站掛個公告,曾經的圖片再去訪問就是默認的404。

        雖然新浪家大業大不是說倒閉就倒閉的,圖片相對穩定可靠,不過新浪的圖片服務器會檢測訪問來源Referer來防止外部網站引用,造成訪問403。

        總結起來就是一句話,圖片還是隨著文章一鍵發布到博客平臺比較好。要丟一起丟~

        心理掙扎

        緣起這個動機,但是下定決心依舊是困難重重。

        我個人是一個Java工程師,雖說搞過Andorid、HTML前端,但對前端深感不適的我果斷放棄了。對于桌面程序開發,我連Swing都不會,造一個Markdown編輯器有點難,何況還要加上這些定制功能。

        猶猶豫豫,還是決定去嘗試一下。于是調研寫跨平臺的一些途徑。

        先嘗試Swing,不過Swing不好實現我期望的一些功能,改成JavaFX倒是可以,不過說實話,寫起來很累,太過繁瑣,就放棄了。最后把目光瞄向electron,就它了,HTML+Js+Css,聽起來就很簡單,事實證明,無論是測試還是打包都很方便。

        決定之后,便開始進行 Electron 的系統學習。

        邁出第一步

        第一步就是安裝 Electron 的本地開發環境,這也是大多數應用開發的第一步。

        你需要安裝 Node.js 在你的本地電腦,Electron 也是依賴于 Node.js 的環境,嚴格來說, Electron 通過將 Chromium 和 Node.js 合并到同一個運行時環境中,并將其打包為Mac,Windows和Linux系統下的應用來實現這一目的。

        關于 Electron 的具體開發流程,這里不再贅述,你完全可以在開發中使用Web前端開發的思維,除了在處理多個窗口之間交互的時候,就不得不了解Eelctron的進程機制。

        主進程和渲染進程

        Electron 運行 package.json 的 main 腳本的進程被稱為主進程。 在主進程中運行的腳本通過創建web頁面來展示用戶界面。 一個 Electron 應用總是有且只有一個主進程。

        由于 Electron 使用了 Chromium 來展示 web 頁面,所以 Chromium 的多進程架構也被使用到。 每個 Electron 中的 web 頁面運行在它自己的渲染進程中。

        在普通的瀏覽器中,web頁面通常在沙盒環境中運行,并且無法訪問操作系統的原生資源。 然而 Electron 的用戶在 Node.js 的 API 支持下可以在頁面中和操作系統進行一些底層交互。

        主進程與渲染進程的區別

        主進程使用 BrowserWindow 實例創建頁面。 每個 BrowserWindow 實例都在自己的渲染進程里運行頁面。 當一個 BrowserWindow 實例被銷毀后,相應的渲染進程也會被終止。

        主進程管理所有的web頁面和它們對應的渲染進程。 每個渲染進程都是獨立的,它只關心它所運行的 web 頁面。

        在頁面中調用與 GUI 相關的原生 API 是不被允許的,因為在 web 頁面里操作原生的 GUI 資源是非常危險的,而且容易造成資源泄露。 如果你想在 web 頁面里使用 GUI 操作,其對應的渲染進程必須與主進程進行通訊,請求主進程進行相關的 GUI 操作。

        主進程與渲染進程通信

        那么進程間如何通訊?

        Electron為主進程( main process)和渲染器進程(renderer processes)通信提供了多種實現方式,如可以使用ipcRenderer 和 ipcMain模塊發送消息,使用 remote模塊進行RPC方式通信。

        你還可以用 Electron 內的 IPC 機制實現。將數據存在主進程的某個全局變量中,然后在多個渲染進程中使用 remote 模塊來訪問它。

        示例代碼:

        // 在主進程中
        global.sharedObject = {
          someProperty: 'default value'
        }
        // 在第一個頁面中
        require('electron').remote.getGlobal('sharedObject').someProperty = 'new value'
        // 在第二個頁面中
        console.log(require('electron').remote.getGlobal('sharedObject').someProperty)

        使用Electron的API

        Electron在主進程和渲染進程中提供了大量API去幫助開發桌面應用程序, 在主進程和渲染進程中,你可以通過require的方式將其包含在模塊中以此,獲取Electron的API

        const electron = require('electron')

        所有Electron的API都被指派給一種進程類型。 許多API只能被用于主進程或渲染進程中,但其中一些API可以同時在上述兩種進程中使用。 每一個API的文檔都將聲明你可以在哪種進程中使用該API。

        Electron中的窗口是使用BrowserWindow類型創建的一個實例, 它只能在主進程中使用。

        // 這樣寫在主進程會有用,但是在渲染進程中會提示'未定義'
        const { BrowserWindow } = require('electron')
        
        const win = new BrowserWindow()

        因為進程之間的通信是被允許的, 所以渲染進程可以調用主進程來執行任務。 Electron通過remote模塊暴露一些通常只能在主進程中獲取到的API。 為了在渲染進程中創建一個BrowserWindow的實例,通常使用remote模塊為中間件:

        // 這樣寫在渲染進程中時行得通的,但是在主進程中是'未定義'
        const { remote } = require('electron')
        const { BrowserWindow } = remote
        
        const win = new BrowserWindow()

        使用Node.js的API

        Electron同時在主進程和渲染進程中對Node.js 暴露了所有的接口。 這里有兩個重要的定義:

        1)所有在Node.js可以使用的API,在Electron中同樣可以使用。 在Electron中調用如下代碼是有用的:

        const fs = require('fs')
        
        const root = fs.readdirSync('/')
        
        // 這會打印出磁盤根級別的所有文件
        // 同時包含'/'和'C:\'。
        console.log(root)

        2)你可以在你的應用程序中使用Node.js的模塊。 選擇您最喜歡的 npm 模塊。 npm 提供了目前世界上最大的開源代碼庫,那里包含良好的維護、經過測試的代碼,提供給服務器應用程序的特色功能也提供給Electron。

        例如,在你的應用程序中要使用官方的AWS SDK,你需要首先安裝它的依賴:

        npm install --save aws-sdk

        然后在你的Electron應用中,通過require引入并使用該模塊,就像構建Node.js應用程序那樣:

        // 準備好被使用的S3 client模塊
        const S3 = require('aws-sdk/clients/s3')

        有一個非常重要的提示: 原生Node.js模塊 (即指,需要編譯源碼過后才能被使用的模塊) 需要在編譯后才能和Electron一起使用。

        最終產品殺青落地

        終于搞明白了 Electron 的應用架構,那么接著就要進入產品的開發階段。比較慶幸的是,ELectron 的UI完全由CSS+HTML組成,這部分可用的框架太多了,我選擇了又老又知名的 BootStarp 框架搭建界面UI,還引用了JS框架JQuery。選擇了 electron-store 作為本地存儲文件,至于最關鍵的Markdown語法解析,對比了一番主流解析框架,最終選擇了 markdown-it。貼一下效果圖:

        這款軟件我給他起名為 JustWrite,意思就是現在就寫,也是在督促自己吧,畢竟猶豫徘徊,等于白來。

        現在軟件的功能除了包含一鍵發布本地文章加本地圖片到博客園、CSDN、SegmentFault、掘金、開源中國等平臺,我還打算將他打造為一個體驗不錯的Markdown寫作軟件?,F在你閱讀的這篇文章,就是我使用 JustWrite 書寫的,使用的字體是我個人喜歡的幼圓體,除此之外,還有六款風格迥異的字體可以切換使用。字號也是可以動態放大或者縮小,還可以關閉右側預覽,專注于寫作,如下圖所示:

        這些截圖是我截屏后使用快捷鍵Ctrl+V一鍵粘貼的,圖片會自動放到當前md文件所在目錄下的picture文件夾內。

        關于 JustWrite 從構思到實踐的心路歷程大致就以上這些了,這次開發 JustWrite 也讓我過了一把產品經理的癮,基本已經滿足了我的日常需求。如果你有更好的想法和創意也可以告訴我,說不定第二天就會實現了。

        Github:https://github.com/onblog/Jus...

        查看原文

        spoonysnail 贊了文章 · 2019-12-23

        我是如何從零學習開發一款跨平臺桌面軟件的(Markdown編輯器)

        作者:薛勤,互聯網從業者,編程愛好者。

        本文首發自公眾號:代碼藝術(ID:onblog)未經許可,不可轉載

        原始沖動

        最近一直在學習 Electron 開發桌面應用程序,目的是想做一個桌面編輯器,雖然一直在使用Typore這款神器,但無奈Typore太過國際化,在國內水土不服,無法滿足我的一些需求。

        比如實現本地圖片上傳到云端(mac版可以借助iPic),無法幫我把本地圖片和文章一起發布到博客園、CSDN、SegmentFault、掘金等國內知名博客平臺,要么使用一些免費或付費的圖床,借助類似iPic的工具,把圖片一鍵上傳到云端。

        我個人也嘗試過七牛云的免費10G存儲空間,但是說實話,這些免費的空間到最后一定是為了讓你成為付費用戶,各種限制各種吐槽在網上很容易可以搜索到。

        免費的圖床如新浪微博等,還算是比較好的圖床工具,相比一些網絡上的壓根不知道啥公司甚至是歸屬個人的免費圖床,新浪應該是比較靠譜的,相對來說可以保證圖片的存活時間,我個人用過一些免費的圖床網站,記得印象深刻的就是服務器出問題,網站掛個公告,曾經的圖片再去訪問就是默認的404。

        雖然新浪家大業大不是說倒閉就倒閉的,圖片相對穩定可靠,不過新浪的圖片服務器會檢測訪問來源Referer來防止外部網站引用,造成訪問403。

        總結起來就是一句話,圖片還是隨著文章一鍵發布到博客平臺比較好。要丟一起丟~

        心理掙扎

        緣起這個動機,但是下定決心依舊是困難重重。

        我個人是一個Java工程師,雖說搞過Andorid、HTML前端,但對前端深感不適的我果斷放棄了。對于桌面程序開發,我連Swing都不會,造一個Markdown編輯器有點難,何況還要加上這些定制功能。

        猶猶豫豫,還是決定去嘗試一下。于是調研寫跨平臺的一些途徑。

        先嘗試Swing,不過Swing不好實現我期望的一些功能,改成JavaFX倒是可以,不過說實話,寫起來很累,太過繁瑣,就放棄了。最后把目光瞄向electron,就它了,HTML+Js+Css,聽起來就很簡單,事實證明,無論是測試還是打包都很方便。

        決定之后,便開始進行 Electron 的系統學習。

        邁出第一步

        第一步就是安裝 Electron 的本地開發環境,這也是大多數應用開發的第一步。

        你需要安裝 Node.js 在你的本地電腦,Electron 也是依賴于 Node.js 的環境,嚴格來說, Electron 通過將 Chromium 和 Node.js 合并到同一個運行時環境中,并將其打包為Mac,Windows和Linux系統下的應用來實現這一目的。

        關于 Electron 的具體開發流程,這里不再贅述,你完全可以在開發中使用Web前端開發的思維,除了在處理多個窗口之間交互的時候,就不得不了解Eelctron的進程機制。

        主進程和渲染進程

        Electron 運行 package.json 的 main 腳本的進程被稱為主進程。 在主進程中運行的腳本通過創建web頁面來展示用戶界面。 一個 Electron 應用總是有且只有一個主進程。

        由于 Electron 使用了 Chromium 來展示 web 頁面,所以 Chromium 的多進程架構也被使用到。 每個 Electron 中的 web 頁面運行在它自己的渲染進程中。

        在普通的瀏覽器中,web頁面通常在沙盒環境中運行,并且無法訪問操作系統的原生資源。 然而 Electron 的用戶在 Node.js 的 API 支持下可以在頁面中和操作系統進行一些底層交互。

        主進程與渲染進程的區別

        主進程使用 BrowserWindow 實例創建頁面。 每個 BrowserWindow 實例都在自己的渲染進程里運行頁面。 當一個 BrowserWindow 實例被銷毀后,相應的渲染進程也會被終止。

        主進程管理所有的web頁面和它們對應的渲染進程。 每個渲染進程都是獨立的,它只關心它所運行的 web 頁面。

        在頁面中調用與 GUI 相關的原生 API 是不被允許的,因為在 web 頁面里操作原生的 GUI 資源是非常危險的,而且容易造成資源泄露。 如果你想在 web 頁面里使用 GUI 操作,其對應的渲染進程必須與主進程進行通訊,請求主進程進行相關的 GUI 操作。

        主進程與渲染進程通信

        那么進程間如何通訊?

        Electron為主進程( main process)和渲染器進程(renderer processes)通信提供了多種實現方式,如可以使用ipcRenderer 和 ipcMain模塊發送消息,使用 remote模塊進行RPC方式通信。

        你還可以用 Electron 內的 IPC 機制實現。將數據存在主進程的某個全局變量中,然后在多個渲染進程中使用 remote 模塊來訪問它。

        示例代碼:

        // 在主進程中
        global.sharedObject = {
          someProperty: 'default value'
        }
        // 在第一個頁面中
        require('electron').remote.getGlobal('sharedObject').someProperty = 'new value'
        // 在第二個頁面中
        console.log(require('electron').remote.getGlobal('sharedObject').someProperty)

        使用Electron的API

        Electron在主進程和渲染進程中提供了大量API去幫助開發桌面應用程序, 在主進程和渲染進程中,你可以通過require的方式將其包含在模塊中以此,獲取Electron的API

        const electron = require('electron')

        所有Electron的API都被指派給一種進程類型。 許多API只能被用于主進程或渲染進程中,但其中一些API可以同時在上述兩種進程中使用。 每一個API的文檔都將聲明你可以在哪種進程中使用該API。

        Electron中的窗口是使用BrowserWindow類型創建的一個實例, 它只能在主進程中使用。

        // 這樣寫在主進程會有用,但是在渲染進程中會提示'未定義'
        const { BrowserWindow } = require('electron')
        
        const win = new BrowserWindow()

        因為進程之間的通信是被允許的, 所以渲染進程可以調用主進程來執行任務。 Electron通過remote模塊暴露一些通常只能在主進程中獲取到的API。 為了在渲染進程中創建一個BrowserWindow的實例,通常使用remote模塊為中間件:

        // 這樣寫在渲染進程中時行得通的,但是在主進程中是'未定義'
        const { remote } = require('electron')
        const { BrowserWindow } = remote
        
        const win = new BrowserWindow()

        使用Node.js的API

        Electron同時在主進程和渲染進程中對Node.js 暴露了所有的接口。 這里有兩個重要的定義:

        1)所有在Node.js可以使用的API,在Electron中同樣可以使用。 在Electron中調用如下代碼是有用的:

        const fs = require('fs')
        
        const root = fs.readdirSync('/')
        
        // 這會打印出磁盤根級別的所有文件
        // 同時包含'/'和'C:\'。
        console.log(root)

        2)你可以在你的應用程序中使用Node.js的模塊。 選擇您最喜歡的 npm 模塊。 npm 提供了目前世界上最大的開源代碼庫,那里包含良好的維護、經過測試的代碼,提供給服務器應用程序的特色功能也提供給Electron。

        例如,在你的應用程序中要使用官方的AWS SDK,你需要首先安裝它的依賴:

        npm install --save aws-sdk

        然后在你的Electron應用中,通過require引入并使用該模塊,就像構建Node.js應用程序那樣:

        // 準備好被使用的S3 client模塊
        const S3 = require('aws-sdk/clients/s3')

        有一個非常重要的提示: 原生Node.js模塊 (即指,需要編譯源碼過后才能被使用的模塊) 需要在編譯后才能和Electron一起使用。

        最終產品殺青落地

        終于搞明白了 Electron 的應用架構,那么接著就要進入產品的開發階段。比較慶幸的是,ELectron 的UI完全由CSS+HTML組成,這部分可用的框架太多了,我選擇了又老又知名的 BootStarp 框架搭建界面UI,還引用了JS框架JQuery。選擇了 electron-store 作為本地存儲文件,至于最關鍵的Markdown語法解析,對比了一番主流解析框架,最終選擇了 markdown-it。貼一下效果圖:

        這款軟件我給他起名為 JustWrite,意思就是現在就寫,也是在督促自己吧,畢竟猶豫徘徊,等于白來。

        現在軟件的功能除了包含一鍵發布本地文章加本地圖片到博客園、CSDN、SegmentFault、掘金、開源中國等平臺,我還打算將他打造為一個體驗不錯的Markdown寫作軟件?,F在你閱讀的這篇文章,就是我使用 JustWrite 書寫的,使用的字體是我個人喜歡的幼圓體,除此之外,還有六款風格迥異的字體可以切換使用。字號也是可以動態放大或者縮小,還可以關閉右側預覽,專注于寫作,如下圖所示:

        這些截圖是我截屏后使用快捷鍵Ctrl+V一鍵粘貼的,圖片會自動放到當前md文件所在目錄下的picture文件夾內。

        關于 JustWrite 從構思到實踐的心路歷程大致就以上這些了,這次開發 JustWrite 也讓我過了一把產品經理的癮,基本已經滿足了我的日常需求。如果你有更好的想法和創意也可以告訴我,說不定第二天就會實現了。

        Github:https://github.com/onblog/Jus...

        查看原文

        贊 34 收藏 15 評論 4

        spoonysnail 發布了文章 · 2019-12-18

        Vue + TypeScript 使用小結

        Vue+TypeScript使用小結

        1. 創建新工程,可以安裝vue cli 3,通過vue create創建

          npm install --global @vue/cli
        2. 必須 添加vue文件聲明shims-vue.d.ts, 用于TypeScript識別vue文件。內容可參考下例。
          1) ts默認不支持導入vue文件, 需要通過以下聲明語句告訴ts按VueConstructor <Vue> 處理vue文件的導入。因此,引用vue文件必須加上.vue后綴。
          2) 當我們需要在Vue上掛載全局變量或方法時,
          e.g. Vue.prototype.$ajax=axios;
          需要在Vue接口上聲明相應變量

          import Vue from 'vue'
          import { AxiosInstance } from 'axios';
          
          declare module 'vue/types/vue' {
              interface Vue {
                  $ajax: AxiosInstance;
                  _: any;
                  $util: any;
              }
          }
          
          declare module '*.vue' {
              export default Vue
          }
        3. 非必須 添加shims-tsx.d.ts文件,可以方便在ts中使用jsx語法。使用jsx語法和使用我們使用模板編寫的方式主要區別在于,使用模板無法獲得靜態類型提示。配合babel-plugin-jsx-v-model插件可以在jsx語法中實現v-model
        4. 使用TypeScript編寫Vue的時候,主要有兩種方法:Vue.extend()和vue-class-component,兩種方法對script部分添加屬性lang=ts
          1) Vue.extend():使用基礎vue構造器,創建一個子類,接近我們以前的vue寫法。用Vue.extend()包裹組件內容就可以了。通過Vue.extend定義組件可以啟用vue的類型推斷,類似下例:

          <script lang="ts">
          import Vue from'vue';
          export default Vue.extend({
              name:'MyComponent',
          });
          </script\>

          2) vue-class-component: vue官方維護的裝飾器,通過各種修飾符進行注明,可以結合vue-property-decorator進行使用

          <script lang="ts">
          import Vue from 'vue'
          import Component from'vue-class-component'
              // @Component修飾符注明了此類為一個Vue組件
              @Component({
                  template:'<button @click="onClick">Click!</button>'
              })
          
          export default class MyComponent extends Vue{
              //初始數據可以直接聲明為實例的屬性
              message:string\='Hello!'
              //組件方法也可以直接聲明為實例的方法
              onClick():void{
                  window.alert(this.message)
              }
          }
          </script\>
        5. prop類型定義:
          1) 對于Array、Object

            mylist:? Array, //錯誤的?。
            mylist:? Array as () => Array<any>, //正確的寫法?
          

          2) props內類型定義的錯誤會影響后續對props和data中定義的字段的引用,出現類似“Property 'xxx' does not exist on type 'Vue'.”錯誤。

        6. vuex使用,
          1) 參考文章

             i.?[https://juejin.im/post/5c46c625e51d456e4138fa82](https://juejin.im/post/5c46c625e51d456e4138fa82)
             ii.[http://www.tvxinternet.com/a/1190000011864013](http://www.tvxinternet.com/a/1190000011864013)
          

          2)不建議使用mapState/mapGetters/mapActions/mapMutations,以明確指定類型。但是store中的類型還是會丟失.

          computed:{
              tags():Array<article.Tag> {
                  returnthis.$store.state.tags;
              },
          },
        7. eslint:
          1) 基礎的typescript eslint配置

          extends:\['@vue/typescript'\],
          rules:{
              // typescript-eslint rules
              '@typescript-eslint/indent':\['error',4\],
              '@typescript-eslint/explicit-function-return-type':0
          },
          parserOptions:{
              parser:'@typescript-eslint/parser'
          },

          2) 如果遇到lint報錯:

          eslint typescript type is defined but never used。

          增加如下lint配置

          overrides:[
              {
                  files:['*.ts','*.tsx','*.vue'],
                  rules:{
                      '@typescript-eslint/no-unused-vars':[2, {args:'none'}]
                  }
              }
          ]
        8. 編譯:使用命令vue add typescript,編譯時會增加typescript編譯。

          vue add typescript
          等同于 npm i -D @vue/cli-plugin-typescript

          不需要我們去設定 webpack loader。vue 自己會處理 ts

        9. vscode:
          1) 在vue文件內的ts使用聲明的全局namespace。vscode會提示cannot find namespace;但是實際上編譯沒問題,ts對于聲明type的校驗也沒問題。暫時只能忽略,沒有找到好的解決方法。
        10. 其他:
          1) webpack alias設置
          vue.config.js 增加配置

          const path = require("path");
          const resolve = dir => path.resolve(__dirname, dir);
          module.exports = {
              chainWebpack: config => {
                  config.resolve.alias.set("@", resolve(src));
                  return config;
              }
          }

          tsconfig.json 增加配置

          {
              "compilerOptions": {
                  "paths": {
                      "@/*": ["src/*"]
                  }
              }
          }

          配置完成后重啟 vscode 生效

        11. 一些報錯處理:
          1)Module 'lodash can only be default-imported using the 'allowSyntheticDefaultImports' flagts(1259)
          解決方案: tsconfig增加配置

          "allowSyntheticDefaultImports": true
        查看原文

        贊 2 收藏 2 評論 0

        spoonysnail 贊了文章 · 2019-12-11

        前端面試題和面試經驗匯總

        贊 126 收藏 100 評論 0

        認證與成就

        • 獲得 4 次點贊
        • 獲得 6 枚徽章 獲得 0 枚金徽章, 獲得 0 枚銀徽章, 獲得 6 枚銅徽章

        擅長技能
        編輯

        (??? )
        暫時沒有

        開源項目 & 著作
        編輯

        (??? )
        暫時沒有

        注冊于 2018-08-03
        個人主頁被 545 人瀏覽

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕