<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>

        senntyou

        senntyou 查看完整檔案

        深圳編輯哈爾濱工業大學  |  電子科學與技術 編輯  |  填寫所在公司/組織 www.senntyou.com/ 編輯
        編輯

        達則兼濟天下,窮則獨善其身。

        個人動態

        senntyou 贊了回答 · 2020-05-27

        html5 canvas繪制圖片模糊的問題

        用了@KevinYue 的例子,感覺不是很好使,我現在的解決方案,不管當前的devicePixelRatio的值是多少,統一將canvasDOM節點的width屬性設置為其csswidth屬性的兩倍,同理將height屬性也設置為cssheight屬性的兩倍,即:

        <canvas width="320" height="180" style="width:160px;height:90px;"></canvas>

        這樣整個canvas的坐標系范圍就擴大為兩倍,但是在瀏覽器的顯示大小沒有變,canvas畫圖的時候,按照擴大化的坐標系來顯示,不清晰的問題就得以改善了。

        關注 25 回答 4

        senntyou 贊了回答 · 2020-05-27

        解決為什么在canvas上畫圖片比設置的尺寸大很多?

        <canvas id='share' canvas-id='share' width="750" height="1334" style='width:750px;height:1334px;'>
        </canvas>
        

        canvas 的大小不是通過 style 設置的,這兩個寬高是兩碼事。

        關注 2 回答 2

        senntyou 發布了文章 · 2020-05-25

        解決 uni-app 微信小程序項目中騰訊統計 mta 不上報數據的問題

        解決 uni-app 微信小程序項目中騰訊統計 mta 不上報數據的問題

        uni-app 微信小程序項目開發中,發現騰訊統計 mta 不上報數據。

        1. 原因

        1. uni-app 框架與 mta 組件都對原生的 Page 對象進行了重寫,在 onLoad 生命周期函數中上報數據,這一點開發者無感知
        2. 因為 uni-app 框架會首先加載自身框架腳本,導致 mta 后加載的腳本對 Page 對象重寫無效(兩者沖突)
        3. 所以,mta 組件中設置了 "autoReport": true 會導致數據不上報("autoReport": false 配置不受影響,因為不需要重寫 Page

        uni-app 框架腳本重寫 Page:

        var MPPage = Page;
        
        Page = function Page() {var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
          initHook('onLoad', options);
          return MPPage(options);
        };

        mta-wechat-analysis.js 重寫 Page:

        function initOnload() {
          var a = Page;
          Page = function (b) {
            var c = b.onLoad;
            b.onLoad = function (a) {
              c && c.call(this, a);
              MTA.Data.lastPageQuery = MTA.Data.pageQuery;
              MTA.Data.pageQuery = a;
              MTA.Data.lastPageUrl = MTA.Data.pageUrl;
              MTA.Data.pageUrl = getPagePath();
              MTA.Data.show = !1;
              MTA.Page.init()
            };
            a(b)
          }
        }

        按理說,前后兩次對 Page 進行重寫,應該是不沖突、都有效的,但 uni-app 在對 Vue 組件轉微信小程序原生組件時,使用了局部封裝的函數,導致后面其他腳本對 Page 的重寫無效

        function createPage(vuePageOptions) {
          return Component(parsePage(vuePageOptions));
        }

        2. 解決

        有兩個解決方案:

        1. mta-wechat-analysis.js 腳本放到 uni-app 框架腳本之前加載,但官方并沒有提供這個功能,所以放棄這個方法
        2. 重寫 Vue 組件,在 Vue 組件里上報統計數據

        因為 Vue 組件是不能重寫生命周期函數的,所以只能重寫調用生命周期函數的方法 Vue.prototype.__call_hook

        mta-wechat-analysis.js:

        - function initOnload() {
        -   var a = Page;
        -   Page = function (b) {
        -     var c = b.onLoad;
        -     b.onLoad = function (a) {
        -       c && c.call(this, a);
        -       MTA.Data.lastPageQuery = MTA.Data.pageQuery;
        -       MTA.Data.pageQuery = a;
        -       MTA.Data.lastPageUrl = MTA.Data.pageUrl;
        -       MTA.Data.pageUrl = getPagePath();
        -       MTA.Data.show = !1;
        -       MTA.Page.init()
        -     };
        -     a(b)
        -   }
        - }
        
        + import Vue from 'vue';
        +
        + function initOnload() {
        +   // 重寫 Vue.prototype.__call_hook 方法
        +   Vue.prototype.__call_hook_proxy = Vue.prototype.__call_hook;
        +   Vue.prototype.__call_hook = function(hook, args) {
        +     if (hook === 'onLoad') {
        +       MTA.Data.lastPageQuery = MTA.Data.pageQuery;
        +       MTA.Data.pageQuery = args;
        +       MTA.Data.lastPageUrl = MTA.Data.pageUrl;
        +       MTA.Data.pageUrl = getPagePath();
        +       MTA.Data.show = !1;
        +       MTA.Page.init();
        +     }
        +     return this.__call_hook_proxy(hook, args);
        +   };
        + }

        改寫后的 mta-wechat-analysis.js 腳本可以點這里下載。

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證

        查看原文

        贊 4 收藏 2 評論 0

        senntyou 發布了文章 · 2019-10-22

        IOS 瀏覽器頁面布局錯位(如:點不到)的分析與解決

        IOS 瀏覽器頁面布局錯位(如:點不到)的分析與解決

        IOS 瀏覽器軟鍵盤的拉起與收縮、微信 IOS 瀏覽器底部導航條的顯示與隱藏,很容易導致頁面布局錯位(相對窗體的絕對定位元素):

        • 明明按鈕在這里,卻要在上面一點兒點擊屏幕才能點到它
        • 明明彈框是居中顯示的,卻向上偏移了很多,導致下面很多空白
        • 明明是固定浮動在某個位置,卻點不到它

        1. Android 與 IOS 的差異

        • 在 Android 中,軟鍵盤的彈起與收縮會觸發 window 對象的 resize 事件,而 IOS 不會
        • 微信 IOS 瀏覽器底部導航條的顯示與隱藏會觸發 window 對象的 resize 事件,而 Android 中沒有底部導航條

        2. IOS 里的一些特性

        • 為了達到極致的體驗,IOS 瀏覽器很多特性是不遵循 W3C 規范的
        • 軟鍵盤的彈起與收縮不會觸發 window 對象的 resize 事件
        • 軟鍵盤收縮后,固定定位的元素處于錯位狀態,需要滑動頁面后才能刷新頁面恢復到正常狀態

        3. 具體情況分析

        不管是 IOS 瀏覽器軟鍵盤的拉起與收縮,還是微信 IOS 瀏覽器底部導航條的顯示與隱藏,都是改變的 window 窗體的大小。

        微信 IOS 瀏覽器底部導航條的顯示與隱藏跟軟鍵盤的拉起與收縮是差不多的,但微信 IOS 瀏覽器底部導航條還有一個很大的特點:

        在單頁面應用(SPA)中,當路由發生變化時,底部導航條會一下子就顯示,而這很難確定是先渲染了頁面還是先顯示了底部導航條,
        這也很容易導致元素布局錯位。

        4. 怎么解決

        4.1 監聽鍵盤彈起與收縮,自動做一些操作

        新建 watch-keyboard.js 腳本,引入到頁面中。

        當頁面中鍵盤彈起時,body 會有 keyboard-active class,可以根據這個隱藏一些元素。

        import {isIos} from '../utils';
        import debounce from 'lodash/debounce';
        
        // 初始高度
        const winHeight = window.innerHeight;
        // 判斷是不是彈起了軟鍵盤
        const judgeDistance = 200;
        
        if (!isIos) {
          window.addEventListener(
            'resize',
            debounce(() => {
              if (window.innerHeight < winHeight - judgeDistance) {
                // 鍵盤彈起
                document.body.classList.add('keyboard-active');
              } else {
                document.body.classList.remove('keyboard-active');
              }
            }, 300),
            !1
          );
        }
        else {
          // IOS 軟鍵盤的彈起與收縮不會觸發 `window` 對象的 `resize` 事件,用定時器實現
        
          // 保證能夠滾動
          document.body.style.minHeight = (winHeight + 2) + 'px';
          // 上兩次高度記錄
          let secondLastWinHeight = winHeight;
          // 上一次高度記錄
          let lastWinHeight = winHeight;
        
          setInterval(() => {
            const newWinHeight = window.innerHeight;
        
            // 變化結束
            if (secondLastWinHeight !== lastWinHeight && lastWinHeight === newWinHeight) {
              if (newWinHeight < winHeight - judgeDistance) {
                // 鍵盤彈起
                document.body.classList.add('keyboard-active');
              } else {
                document.body.classList.remove('keyboard-active');
                // window 需要滾動一下,讓頁面刷新一下,否則彈框會出現錯位的問題
                window.scrollTo(0, window.scrollY ? window.scrollY - 1 : 1);
              }
            }
        
            secondLastWinHeight = lastWinHeight;
            lastWinHeight = newWinHeight;
          }, 300); // 可以根據需要調整間隔時間(越小越精確)
        }

        4.2 監聽窗體大小變化,執行一個回調,做更多操作

        當軟鍵盤彈起時,又點擊了一個按鈕,然后顯示彈框(如:從底部向上彈出)的時候,這個時候就需要等待軟鍵盤收起之后,IOS 刷新屏幕之后,再顯示彈框。

        新建 wait-for-stable-win-height.js 腳本,引入到頁面中。

        import { isIos } from '../utils';
        
        /**
         * 等待 window 高度不變了之后執行一個回調函數
         *
         * @param onComplete 完成的回調
         * @param delay 延遲多少時間再判斷
         * @param interval 定時器間隔時間
         */
        export default ({ onComplete, delay = 200, interval = 50 }) => {
          setTimeout(() => {
            let winHeight = window.innerHeight;
            const timer = setInterval(() => {
              const newWinHeight = window.innerHeight;
        
              if (winHeight === newWinHeight) {
                clearInterval(timer);
                if (onComplete) {
                  if (!isIos) {
                    setTimeout(() => {
                      onComplete();
                    }, 100);
                    return;
                  }
        
                  // window 需要滾動一下,讓頁面刷新一下,否則彈框會出現錯位的問題
                  window.scrollTo(0, window.scrollY ? window.scrollY - 1 : 1);
                  setTimeout(() => {
                    onComplete();
                  }, 200);
                }
              } else {
                winHeight = newWinHeight;
              }
            }, interval);
          }, delay);
        };

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證

        查看原文

        贊 16 收藏 14 評論 0

        senntyou 發布了文章 · 2019-08-13

        批量轉換 AMD 與 CommonJs 模塊到 ES 模塊

        批量轉換 AMD 與 CommonJs 模塊到 ES 模塊

        就前端而言,ES6 的模塊化寫法已經是主流了,但很多老的項目都是用 AMD 或者 CommonJs 規范寫的。
        由于 Webpack 現在已經慢慢不太支持 AMD 或者 CommonJs 規范的代碼,所以需要把這兩種規范的代碼都轉換成 ES6 規范的代碼。
        網上搜了一下,并沒有這之類的工具,我就自己寫了一個:conv-mod.

        安裝

        npm install conv-mod -g

        使用

        conv-mod [options] <dir> [extraDirs...]

        參數

        • -f, --filter <filter>: 查詢某個字符串,過濾文件
        • -r, --regular: 當查詢某個字符串,過濾文件時,把查詢字符串當作正則匹配
        • --amd: 轉化 AMD 模塊
        • --cjs: 轉化 CommonJs 模塊

        示例

        轉換 src 目錄下的 AMD 與 CommonJs 代碼

        conv-mod src --amd --cjs

        轉換 src/dir1,src/dir2 目錄下的 AMD 代碼

        conv-mod src/dir1 src/dir2 --amd

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        贊 6 收藏 4 評論 1

        senntyou 發布了文章 · 2019-06-20

        再談前端項目的組件化

        再談前端項目的組件化

        之前詳細聊過的前端項目的組件化,可以參考 組件化私有 npm 倉庫,今天來更進一步的說說前端項目的組件化。

        1. 之前的組件化

        目錄結構:

        -project1     # 項目1
        -project2     # 項目2
        -component1   # 組件1
        -component2   # 組件2

        project1package.json

        {
          "dependencies": {
            "@yourCompany/component1": "^0.0.1",
            "@yourCompany/component2": "^0.0.1"
          }
        }

        在代碼中使用:

        import component1 from '@yourCompany/component1';

        2. 之前的組件化方式存在的問題

        1. 更新組件比較麻煩,特別是對于一些與業務耦合比較深的組件,頻繁更新會比較頭疼
        2. 組件太多的時候,管理起來就感覺比較累,因為每個組件都是一個單獨的項目,都有一套獨立的構建環境
        3. 對于有些代碼量小的組件,做一個單獨的項目,實在有點大才小用

        3. 另外的項目組件化方式

        針對上面講到的問題,另一種方式可以很好的解決:

        目錄結構:

        -project1     # 項目1
        -project2     # 項目2
        -components   # 組件集合項目

        components 組件集合項目的目錄結構:

        - src/             # 源代碼目錄
          - component1     # 組件1
          - component2     # 組件2
          - component3     # 組件3
          - ...
        
        - package.json
        - README.md
        - CHANGELOG.md
        - .eslintrc.js
        - .stylelintrc.js
        - .prettierrc.js
        - ...

        components 目錄軟鏈接 project1 目錄下:

        (注意: project1.gitignore 需加上 /components

        # 以下是 linux 命令,windows 類似
        cd project1
        ln -s ../components ./

        project1 項目的目錄結構:

        - src/             # 源代碼目錄
        - components/      # 組件項目目錄(軟鏈接)
        
        - package.json
        - README.md
        - CHANGELOG.md
        - .eslintrc.js
        - .stylelintrc.js
        - .prettierrc.js
        - ...

        在代碼中使用:

        import component1 from 'relative/path/to/components/src/component1';

        4. 兩種方式的選擇

        上面的兩種方式各有各的優勢,可以配合一起使用。

        大的、不常更新的組件可以使用 npm 包的方式,小的、常更新的可以使用軟鏈接項目的方式。

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        贊 13 收藏 11 評論 0

        senntyou 發布了文章 · 2019-06-06

        js 函數參數推薦書寫方式 ({ param1, param2, ... })

        js 函數參數推薦書寫方式 ({ param1, param2, ... })

        編程語言函數(包括對象的方法)參數的取值方式大致可以分為兩種:按序取值與按名取值。

        一般編程語言都是按序取值,比如 C、Java、JavaScript 等,少數語言支持按名取值,比如 Groovy。

        1. 按序取值

        按照順序,挨個取值,每個參數的順序是固定的。

        const func = (param1, param2, ...) => { ... }
        
        func(1, 2, ...)

        2. 按名取值

        按照名稱取值,可以任意安排各個參數的順序。

        以下語法并不存在,只是作為講解生造的
        const func = (param1: value1, param2: value2, ...) => { ... }
        
        func(param1: 1, param2: 2); // ok 
        func(param2: 2, param1: 1); // ok again 

        3. js 的按名取值

        JavaScript 語言本身并不支持按名取值,但結合 ES6 的解構賦值,可以模擬函數參數的按名取值。

        const func = ({ param1, param2, ... }) => { ... }
        
        func({ param1: 1, param2: 2, ... });

        但這種方式如果不傳參數調用 func() 就會報錯,需要 func({}) 這樣調用才表示什么參數都不傳。

        為了兼容這種方式,可以這樣做:

        const func = ({ param1, param2, ... } = {}) => { ... }
        
        func();   // ok
        func({}); // ok again

        4. 為什么推薦使用按名取值的方式

        按名取值最大的好處是可以隨意安排參數的順序,有利于擴展,特別是對 API 接口來說。

        比如:

        export const dialog = (title, content, confirmCallback, cancelCallback) => { ... }

        比如上面的函數中,大部分情況下我只用 content, confirmCallback,那么我就需要這樣做:

        dialog(null, 'content', () => { ... });

        如果我需要擴展一個參數 icon, 那么為了兼容以前的版本,我只能加在最后面:

        export const dialog = (title, content, confirmCallback, cancelCallback, icon) => { ... }

        現在,大部分情況下我只用 content, confirmCallback, icon,那么我就需要這樣做:

        dialog(null, 'content', () => { ... }, null, 'icon');

        如此,便很麻煩,不利于擴展。

        如果使用按名取值的方式,便迎刃而解:

        export const dialog = ({title, content, confirmCallback, cancelCallback} = {}) => { ... }
        
        // 擴展 icon
        export const dialog = ({title, content, icon, confirmCallback, cancelCallback} = {}) => { ... }
        dialog({content: 'content', confirmCallback: () => { ... }});
        
        dialog({content: 'content', icon: 'icon', confirmCallback: () => { ... }});

        5. 大家可能的反駁

        有人可能會說,可以這樣做:

        export const dialog = (title, content, confirmCallback, cancelCallback) => { 
          if (typeof content === 'function') {
            cancelCallback = confirmCallback;
            confirmCallback = content;
            content = title;
          }
          
          ...
        }

        對于這種方式,我只想說:兄弟,簡潔一點不好嗎?

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        贊 12 收藏 6 評論 2

        senntyou 發布了文章 · 2019-05-30

        細說 Vue 組件的服務器端渲染

        細說 Vue 組件的服務器端渲染

        聲明:需要讀者對 NodeJs、Vue 服務器端渲染有一定的了解

        現在,前后端分離與客戶端渲染已經成為前端開發的主流模式,絕大部分的前端應用都適合用這種方式來開發,又特別是 React、Vue 等組件技術的發展,更是使這種方式深入人心。

        但有一些應用,客戶端渲染就會遇到一些問題了:

        1. 需要做 SEO(搜索引擎優化),但客戶端渲染的 html 中幾乎沒有可用的信息
        2. 需要首屏快速加載,但客戶端渲染一般是長時間的加載動畫或者白屏

        如果能把客戶端渲染的組件化技術(React、Vue 等)與傳統的后端渲染的方式有效的結合起來,兩者兼具,那就太完美了。

        所以,這次就來聊聊 Vue 組件的服務器端渲染。

        根據社區現有的一些方案,結合自己的實踐,針對團隊技術力量的不同,說說不同應用場景選擇方案時的優先級。

        1. NodeJs 渲染中間層

        一般前后端的工作流是 后端 -> 前端。

        傳統的后端渲染模式是后端負責包括 url、接口、模板渲染等,前端與后端耦合在一起,當然這種方式正在慢慢的退出歷史舞臺。

        主流的客戶端渲染則是后端只提供接口(如有需要,可以提供必要的 url),前端與后端只通過接口交流數據,路由與渲染都在前端完成。

        而 NodeJs 渲染中間層的工作流則是 后端 -> NodeJs -> 前端(NodeJs 渲染中間層由前端開發人員掌握)。

        圖片描述

        這種模式下,后端只提供接口,傳統的服務器端路由(url)、模板渲染則都有 NodeJs 層接管。這樣,前端開發人員可以自由的決定哪些組件需要在服務器端渲染,哪些組件可以放在客戶端渲染,前后端完全解耦,但又保留了服務器端渲染的功能。

        這種方案最成熟的是 nuxt.js。

        如果有需要,大家可以自己去 nuxt.js 官方文檔 看看具體的使用方法和詳細的功能。

        應該說,這種方式是目前最完美的一種方案,但也有一些隱患:

        1. 增加了一個 NodeJs 中間層,應用性能會有所降低
        2. 增加了架構的復雜度、不穩定性,也降低了應用的安全性
        3. 對于高并發應用,NodeJs 層會很容易形成瓶頸
        4. 對開發人員要求高了很多

        所以,這種方式適合對并發量、安全性、穩定性等要求不高,但又需要做 SEO 或首屏快速加載的頁面。

        當然,如果你能夠自己改造相關的工具,就另當別論了。

        2. 保留后端模板渲染

        當不能使用 NodeJs 中間層時,而又要達到 SEO 與首屏快速響應的目的時,在傳統的后端模板渲染的基礎上,就需要對前端的頁面加以適當的改造。

        2.1 首屏快速響應

        首屏快速響應就意味著首屏渲染所需的數據是跟 HTML 文件一起到達瀏覽器的,這些數據當前是由后端模板引擎嵌入到 HTML 頁面中的。

        以 Java 的 freemarker 模板引擎為例:

        html 中以 script 的方式獲取模板的數據,這樣就算是在本地調試、開發,也不會報錯)。

        <script>
          window.globalData = {
            stringValue: '${stringValueTplName}',
            intValue: parseInt('${intValueTplName}', 10),
          };
        </script>

        如果是復雜的 Json 數據或者其他復雜的模板數據(比如列表數據),則可以像下面這樣接收:

        <script type="text/tpl" id="tpl-script-json">
          window.tmpData = {
            jsonValue: ${jsonValueTplName},
          };
        </script>
        
        <script>
          try {
            eval(document.getElementById('tpl-script-json').innerText);
          } catch (e) {
            window.tmpData = { jsonValue: {} };
          }
          
          window.globalData = {
            jsonValue: window.tmpData.jsonValue,
          };
        </script>

        這樣,你就可以在組件里使用 window.globalData 的數據了,而不用另外用接口獲取數據,達到加快首屏渲染的目的,而且本地開發、調試也不會報錯。

        如果你使用了本地數據 Mock 功能,也可以很容易的與這種方式結合在一起,只要稍加改造:

        1. 在代碼中定義本地和服務器兩個環境,本地環境使用 Mock 數據,服務器環境使用 window.globalData
        2. 可以使用 see-ajax, see-fetch 來簡化這種方式的開發

        此外,還有一些措施來進一步加快首屏渲染:

        1. 盡量減少首屏加載的腳本文件大小,其他腳本可以按需加載
        2. 如果需要,可以將 CSS、JS 內容注入到 HTML 中,這樣就只會發起一個請求,也可以加快加載速度

        2.2 SEO 優化

        在上面加載首屏渲染的基礎上,對于 SEO 優化也可以做相應的改造。

        其實,在客戶端渲染已慢慢成為主流開發模式的同時,搜索引擎也在跟進這種變化。

        截至目前,Google 和 Bing 可以很好對同步 JavaScript 應用程序進行索引,也就是說,即使是客戶端渲染,但只要是同步數據渲染(非 Ajax 獲取數據,比如模板數據),搜索引擎也能抓取到相應的 HTML 片段。

        (國內的百度搜索與360搜索等暫時還沒有跟進動態)

        但為了兼容所有的搜索引擎,可以像下面改造:

        1. 先由后端模板引擎渲染一些 HTML 片段,僅給搜索引擎抓取,不作為給用戶展示的頁面
        2. 然后再由客戶端渲染同步或異步的數據給用戶展示真正的頁面
        <div>
          <!-- 這里放置由后端模板引擎渲染的專給搜索引擎抓取的片段,用戶不可見 -->
        </div>
        
        <script>
          // 接收同步數據
          window.globalData = {
            stringValue: '${stringValueTplName}',
            intValue: parseInt('${intValueTplName}', 10),
          };
        </script>

        3. 導出靜態 html

        如果頁面沒有動態數據,那就好辦了,直接把組件導出為靜態 html,然后由客戶端激活。

        具體過程可以參考 官方文檔。

        這種方案比較好的是 nuxt.jsgenerate 靜態 HTML 文件。

        目錄結構:

        - pages/                # 頁面結構目錄
          - index.vue 
          - second.vue
          - ... 
        - nuxt.config.js        # 配置文件
        - package.json
        
        - dist                  # 導出靜態 HTML 文件的默認目錄 

        導出靜態 HTML 文件

        npx nuxt generate

        如果一個項目里有多個 pages,可以這樣構建:

        目錄結構:

        - nuxt.config.js        # 配置文件
        - package.json
        
        - src/
          - home/               # home 頁面 
            - pages/            # 頁面結構目錄
              - index.vue 
              - second.vue
              - ...
              
            - dist              # 導出靜態 HTML 文件的默認目錄
          - about/              # about 頁面 
            - pages/            # 頁面結構目錄
              - index.vue 
              - second.vue
              - ...
              
            - dist              # 導出靜態 HTML 文件的默認目錄   

        導出靜態 HTML 文件

        npx nuxt generate src/home -c ../../nuxt.config.js    # home 頁面
        npx nuxt generate src/about -c ../../nuxt.config.js   # about 頁面

        除了上面提到的這些方式外,當然還有其他的方式,比如:

        1. 使用 Chrome Headless 模式獲取組件的靜態 HTML,參考 react-snap, puppeteer
        2. 官方 vue-server-renderer 導出靜態 HTML

        4. 總結

        因為模式的改變,服務器端渲染與傳統的后端模板渲染工作方式有很大的不同,所以在開發時需要與后端開發人員做好溝通,避免認知上的不同導致協作不協調。

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        贊 15 收藏 13 評論 0

        senntyou 發布了文章 · 2019-03-29

        如何查看一個 js, ts 文件模塊的依賴樹

        如何查看一個 js, ts 文件模塊的依賴樹

        最近接手一個別人的頁面,代碼很是凌亂與龐雜,當我在增加功能時,發現我添加的模塊與原有的模塊有沖突,但不知道那個原有的模塊在什么位置,就得到處找。

        但發現這種方式太笨拙了,網上也沒有找到相應的工具,索性我就自己寫了一個工具。

        項目地址:sdep。

        安裝

        npm install sdep -g

        使用

        sdep [options] <file>

        常用命令行參數

        • -q, --query <query>: 查找某個模塊的依賴鏈(如:-q react
        • -r, --regular: 把 query 當作正則來匹配(如:-q 'react|react-dom' -r
        • -i, --ignore: 不顯示 node_modules 里的文件

        使用的第三方庫

        例子

        查看一個文件的依賴結構

        sdep example/index.js
        example/index.js
        ├ example/css/css.css
        ├ example/css/scss.scss
        ├ example/css/less.less
        ├ example/jsx.jsx
        | ├ node_modules/react/index.js
        | | ├ node_modules/react/cjs/react.production.min.js
        | | | └ node_modules/object-assign/index.js
        | | └ node_modules/react/cjs/react.development.js
        | |   ├ node_modules/object-assign/index.js
        | |   └ node_modules/prop-types/checkPropTypes.js
        | |     └ node_modules/prop-types/lib/ReactPropTypesSecret.js
        | ├ node_modules/react-dom/index.js
        | | ├ node_modules/react-dom/cjs/react-dom.production.min.js
        | | | ├ node_modules/react/index.js
        | | | | ├ node_modules/react/cjs/react.production.min.js
        | | | | | └ node_modules/object-assign/index.js
        | | | | └ node_modules/react/cjs/react.development.js
        | | | |   ├ node_modules/object-assign/index.js
        | | | |   └ node_modules/prop-types/checkPropTypes.js
        | | | |     └ node_modules/prop-types/lib/ReactPropTypesSecret.js
        | | | ├ node_modules/object-assign/index.js
        | | | └ node_modules/scheduler/index.js
        | | |   ├ node_modules/scheduler/cjs/scheduler.production.min.js
        | | |   └ node_modules/scheduler/cjs/scheduler.development.js
        | | └ node_modules/react-dom/cjs/react-dom.development.js
        | |   ├ node_modules/react/index.js
        | |   | ├ node_modules/react/cjs/react.production.min.js
        | |   | | └ node_modules/object-assign/index.js
        | |   | └ node_modules/react/cjs/react.development.js
        | |   |   ├ node_modules/object-assign/index.js
        | |   |   └ node_modules/prop-types/checkPropTypes.js
        | |   |     └ node_modules/prop-types/lib/ReactPropTypesSecret.js
        | |   ├ node_modules/object-assign/index.js
        | |   ├ node_modules/prop-types/checkPropTypes.js
        | |   | └ node_modules/prop-types/lib/ReactPropTypesSecret.js
        | |   ├ node_modules/scheduler/index.js
        | |   | ├ node_modules/scheduler/cjs/scheduler.production.min.js
        | |   | └ node_modules/scheduler/cjs/scheduler.development.js
        | |   └ node_modules/scheduler/tracing.js
        | |     ├ node_modules/scheduler/cjs/scheduler-tracing.production.min.js
        | |     └ node_modules/scheduler/cjs/scheduler-tracing.development.js
        | └ example/wel.jsx
        |   └ node_modules/react/index.js
        |     ├ node_modules/react/cjs/react.production.min.js
        |     | └ node_modules/object-assign/index.js
        |     └ node_modules/react/cjs/react.development.js
        |       ├ node_modules/object-assign/index.js
        |       └ node_modules/prop-types/checkPropTypes.js
        |         └ node_modules/prop-types/lib/ReactPropTypesSecret.js
        └ example/async/index.js
          └ example/async/index.css

        查看一個文件包含 react 的依賴鏈

        sdep example/index.js -q react
        example/index.js
        └ example/jsx.jsx
          └ node_modules/react/index.js
        example/index.js
        └ example/jsx.jsx
          └ node_modules/react-dom/index.js
        example/index.js
        └ example/jsx.jsx
          └ example/wel.jsx
            └ node_modules/react/index.js

        查看一個文件包含 lessscss 的依賴鏈

        sdep example/index.js -q 'less|scss' -r
        example/index.js
        └ example/css/scss.scss
        example/index.js
        └ example/css/less.less

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        贊 16 收藏 13 評論 0

        senntyou 評論了文章 · 2019-03-22

        從 0 到 1 再到 100, 搭建、編寫、構建一個前端項目

        從 0 到 1 再到 100, 搭建、編寫、構建一個前端項目

        1. 選擇現成的項目模板還是自己搭建項目骨架

        搭建一個前端項目的方式有兩種:選擇現成的項目模板、自己搭建項目骨架。

        選擇一個現成項目模板是搭建一個項目最快的方式,模板已經把基本的骨架都搭建好了,你只需要向里面填充具體的業務代碼,就可以通過內置的工具與命令構建代碼、部署到服務器等。

        一般來說,一個現成的項目模板會預定義一定的目錄結構、書寫方式,在編寫項目代碼時需要遵循相應的規范;也會內置必要的工具,比如 .editorconfig、eslint、stylelint、prettier、husky、lint-staged 等;也會內置必要的命令(package.json | scripts),比如 本地開發:npm run dev、本地預覽:npm run start、構建:npm run build、部署:npm run deploy 等。

        社區比較好的項目模板:

        這些模板的使用又分為兩種:使用 git 直接克隆到本地、使用命令行創建。

        (使用現有模板構建的項目,可以跳過第 2 ~ 7 步)

        1.1 使用 git 直接克隆到本地

        這是一種真正意義上的模板,可以直接到模板項目的 github 主頁,就能看到整個骨架,比如 react-boilerplate、ant-design-pro、vue-element-admin、react-starter-kit。

        react-boilerplate 為例:

        克隆到本地:

        git clone --depth=1 https://github.com/react-boilerplate/react-boilerplate.git <你的項目名字>

        切換到目錄下:

        cd <你的項目名字>

        一般來說,接下來運行 npm run install 安裝項目的依賴后,就可以運行;有些模板可能有內置的初始化命令,比如 react-boilerplate

        npm run setup

        啟動應用:

        npm start

        這時,就可以在瀏覽器中預覽應用了。

        1.2 使用命令行創建

        這種方式需要安裝相應的命令,然后由命令來創建項目。

        create-react-app 為例:

        安裝命令:

        npm install -g create-react-app

        創建項目:

        create-react-app my-app

        運行應用:

        cd my-app
        npm start

        1.3 自己搭建項目骨架

        如果你需要定制化,可以選擇自己搭建項目的骨架,但這需要開發者對構建工具如 webpack、npm、node 及其生態等有相當的了解與應用,才能完美的把控整個項目。

        下面將會一步一步的說明如何搭建一個定制化的項目骨架。

        2. 選擇合適的規范來寫代碼

        js 模塊化的發展大致有這樣一個過程 iife => commonjs/amd => es6,而在這幾個規范中:

        • iife: js 原生支持,但一般不會直接使用這種規范寫代碼
        • amd: requirejs 定義的加載規范,但隨著構建工具的出現,便一般不會用這種規范寫代碼
        • commonjs: node 的模塊加載規范,一般會用這種規范寫 node 程序
        • es6: ECMAScript2015 定義的模塊加載規范,需要轉碼后瀏覽器才能運行

        這里推薦使用 es6 的模塊化規范來寫代碼,然后用工具轉換成 es5 的代碼,并且 es6 的代碼可以使用 Tree shaking 功能。

        參考:

        3. 選擇合適的構建工具

        對于前端項目來說,構建工具一般都選用 webpack,webpack 提供了強大的功能和配置化運行。如果你不喜歡復雜的配置,可以嘗試 parcel。

        參考:

        4. 確定是單頁面應用(SPA)還是多頁面應用

        因為單頁面應用與多頁面應用在構建的方式上有很大的不同,所以需要從項目一開始就確定,使用哪種模式來構建項目。

        4.1 多頁面應用

        傳統多頁面是由后端控制一個 url 對應一個 html 文件,頁面之間的跳轉需要根據后端給出的 url 跳轉到新的 html 上。比如:

        http://www.example.com/page1 -> path/to/page1.html
        http://www.example.com/page2 -> path/to/page2.html
        http://www.example.com/page3 -> path/to/page3.html

        這種方式的應用,項目里會有多個入口文件,搭建項目的時候就需要對這種多入口模式進行封裝。另外,也可以選擇一些封裝的多入口構建工具,如 lila。

        4.2 單頁面應用

        單頁面應用(single page application),就是只有一個頁面的應用,頁面的刷新和內部子頁面的跳轉完全由 js 來控制。

        一般單頁面應用都有以下幾個特點:

        • 本地路由,由 js 定義路由、根據路由渲染頁面、控制頁面的跳轉
        • 所有文件只會加載一次,最大限度重用文件,并且極大提升加載速度
        • 按需加載,只有真正使用到頁面的時候,才加載相應的文件

        這種方式的應用,項目里只有一個入口文件,便無需封裝。

        參考:

        5. 選擇合適的前端框架與 UI 庫

        一般在搭建項目的時候就需要定下前端框架與 UI 庫,因為如果后期想更換前端框架和 UI 庫,代價是很大的。

        比較現代化的前端框架:

        一些不錯的組合:

        參考:

        6. 定好目錄結構

        一個好的目錄結構對一個好的項目而言是非常必要的。

        一個好的目錄結構應當具有以下的一些特點:

        1. 解耦:代碼盡量去耦合,這樣代碼邏輯清晰,也容易擴展
        2. 分塊:按照功能對代碼進行分塊、分組,并能快捷的添加分塊、分組
        3. 編輯器友好:需要更新功能時,可以很快的定位到相關文件,并且這些文件應該是很靠近的,而不至于到處找文件

        比較推薦的目錄結構:

        多頁面應用

        |-- src/ 源代碼目錄
        
            |-- page1/ page1 頁面的工作空間(與這個頁面相關的文件都放在這個目錄下)
                |-- index.html html 入口文件
                |-- index.js js 入口文件
                |-- index.(css|less|scss) 樣式入口文件
                |-- html/ html 片段目錄
                |-- (css|less|scss)/ 樣式文件目錄
                |-- mock/ 本地 json 數據模擬
                |-- images/ 圖片文件目錄
                |-- components/ 組件目錄(如果基于 react, vue 等組件化框架)
                |-- ...
                
            |-- sub-dir/ 子目錄
                |-- page2/ page2 頁面的工作空間(內部結構參考 page1)
                    |-- ...
                
            |-- ...
            
        |-- html/ 公共 html 片段
        |-- less/ 公共 less 目錄
        |-- components/ 公共組件目錄
        |-- images/ 公共圖片目錄
        |-- mock/ 公共 api-mock 文件目錄
        |-- ...

        單頁面應用

        |-- src/ 源代碼目錄
            |-- page1/ page1 頁面的工作空間
                |-- index.js 入口文件
                |-- services/ service 目錄
                |-- models/ model 目錄
                |-- mock/ 本地 json 數據模擬
                |-- images/ 圖片文件目錄
                |-- components/ 組件目錄(如果基于 react, vue 等組件化框架)
                |-- ...
                
            |-- module1/ 子目錄
                |-- page2/ page2 頁面的工作空間(內部結構參考 page1)
                
            |-- ...
            
        |-- images/ 公共圖片目錄
        |-- mock/ 公共 api-mock 文件目錄
        |-- components/ 公共組件目錄   
        |-- ... 

        參考:

        7. 搭建一個好的腳手架

        搭建一個好的腳手架,能夠更好的編寫代碼、構建項目等。

        可以查看 搭建自己的前端腳手架 了解一些基本的腳手架文件與工具。

        比如:

        |-- /                              項目根目錄
            |-- src/                       源代碼目錄
            |-- package.json               npm 項目文件
            |-- README.md                  項目說明文件
            |-- CHANGELOG.md               版本更新記錄
            |-- .gitignore                 git 忽略配置文件
            |-- .editorconfig              編輯器配置文件
            |-- .npmrc                     npm 配置文件
            |-- .npmignore                 npm 忽略配置文件
            |-- .eslintrc                  eslint 配置文件
            |-- .eslintignore              eslint 忽略配置文件
            |-- .stylelintrc               stylelint 配置文件
            |-- .stylelintignore           stylelint 忽略配置文件
            |-- .prettierrc                prettier 配置文件
            |-- .prettierignore            prettier 忽略配置文件
            
            |-- .babelrc                   babel 配置文件
            |-- webpack.config.js          webpack 配置文件
            |-- rollup.config.js           rollup 配置文件
            |-- gulpfile.js                gulp 配置文件
            
            |-- test/                      測試目錄
            |-- docs/                      文檔目錄
            |-- jest.config.js             jest 配置文件
            |-- .gitattributes             git 屬性配置
        • .editorconfig: 用這個文件來統一不同編輯器的一些配置,比如 tab 轉 2 個空格、自動插入空尾行、去掉行尾的空格等,http://editorconfig.org
        • eslint、stylelint、prettier: 規范化代碼風格、優化代碼格式等
        • husky、lint-staged: 在 git 提交之前對代碼進行審查,否則不予提交
        • .gitlab-ci.yml: gitlab ci 持續集成服務

        參考:

        =================================================

        到這里為止,一個基本的項目骨架就算搭建好了。

        8. 使用版本控制系統管理源代碼(git)

        項目搭建好后,需要一個版本控制系統來管理源代碼。

        比較常用的版本管理工具有 git、svn,現在一般都用 git。

        一般開源的項目可以托管到 http://github.com,私人的項目可以托管到 https://gitee.com、https://coding.net/,而企業的項目則需要自建版本控制系統了。

        自建版本控制系統主要有 gitlab、gogs、giteagitlab 是由商業驅動的,比較穩定,社區版是免費的,一般建議選用這個;gogs, gitea 是開源的項目,還不太穩定,期待進一步的更新。

        所以,git + gitlab 是不錯的配合。

        9. 編寫代碼

        編寫代碼時,js 選用 es6 的模塊化規范來寫(如果喜歡用 TypeScript,需要加上 ts-loader),樣式可以用 less、scss、css 來寫。

        js 模塊文件時,注釋可以使用 jsdoc 的規范來寫,如果配置相應的工具,可以將這些注釋導出接口文檔。

        因為腳手架里有 husky、lint-staged 的配合,所以每次提交的代碼都會進行代碼審查與格式優化,如果不符合規范,則需要把不規范的代碼進行修改,然后才能提交到代碼倉庫中。

        比如 console.log(haha.hehe); 這段代碼就會遇到錯誤,不予提交:

        圖片描述

        這個功能定義在 package.json 中:

        {
          "devDependencies": {             工具依賴
            "babel-eslint": "^8.2.6",
            "eslint": "^4.19.1",
            "husky": "^0.14.3",
            "lint-staged": "^7.2.0",
            "prettier": "^1.14.0",
            "stylelint": "^9.3.0",
            "eslint-config-airbnb": "^17.0.0",
            "eslint-config-prettier": "^2.9.0",
            "eslint-plugin-babel": "^5.1.0",
            "eslint-plugin-import": "^2.13.0",
            "eslint-plugin-jsx-a11y": "^6.1.0",
            "eslint-plugin-prettier": "^2.6.2",
            "eslint-plugin-react": "^7.10.0",
            "stylelint-config-prettier": "^3.3.0",
            "stylelint-config-standard": "^18.2.0"
          },
          "scripts": {                     可以添加更多命令
            "precommit": "npm run lint-staged",
            "prettier": "prettier --write \"./**/*.{js,jsx,css,less,sass,scss,md,json}\"",
            "eslint": "eslint .",
            "eslint:fix": "eslint . --fix",
            "stylelint": "stylelint \"./**/*.{css,less,sass,scss}\"",
            "stylelint:fix": "stylelint \"./**/*.{css,less,sass,scss}\" --fix",
            "lint-staged": "lint-staged"
          },
          "lint-staged": {                 對提交的代碼進行檢查與矯正
            "**/*.{js,jsx}": [
              "eslint --fix",
              "prettier --write",
              "git add"
            ],
            "**/*.{css,less,sass,scss}": [
              "stylelint --fix",
              "prettier --write",
              "git add"
            ],
            "**/*.{md,json}": [
              "prettier --write",
              "git add"
            ]
          }
        }
        • 如果你想禁用這個功能,可以把 scripts"precommit" 改成 "http://precommit"
        • 如果你想自定 eslint 檢查代碼的規范,可以修改 .eslintrc, .eslintrc.js 等配置文件
        • 如果你想自定 stylelint 檢查代碼的規范,可以修改 .stylelintrc, .stylelintrc.js 等配置文件
        • 如果你想忽略某些文件不進行代碼檢查,可以修改 .eslintignore, .stylelintignore 配置文件

        參考:

        10. 組件化

        當項目擁有了一定量的代碼之后,就會發現,有些代碼是很多頁面共用的,于是把這些代碼提取出來,封裝成一個組件,供各個地方使用。

        當擁有多個項目的時候,有些組件需要跨項目使用,一種方式是復制代碼到其他項目中,但這種方式會導致組件代碼很難維護,所以,一般是用另一種方式:組件化。

        組件化就是將組件獨立成一個項目,然后在其他項目中安裝這個組件,才能使用。

        一般組件化會配合私有 npm 倉庫一起用。

        |-- project1/ 項目1
            |-- package.json
            
        |-- project2/ 項目2
            |-- package.json    
        
        |-- component1/ 組件1
            |-- package.json
        
        |-- component2/ 組件2
            |-- package.json

        project1 中安裝 component1, component2 組件:

        # package.json
        {
          "dependencies": {
            "component1": "^0.0.1",
            "component2": "^0.0.1"
          }
        }
        import compoennt1 from 'compoennt1';
        import compoennt2 from 'compoennt2';

        如果想要了解怎樣寫好一個組件(npm package),可以參考 從 1 到完美,寫一個 js 庫、node 庫、前端組件庫。

        參考:

        11. 測試

        測試的目的在于能以最少的人力和時間發現潛在的各種錯誤和缺陷,這在項目更新、重構等的過程中尤其重要,因為每當更改一些代碼后,你并不知道這些代碼有沒有問題、會不會影響其他的模塊。如果有了測試,運行一遍測試用例,就知道更改的代碼有沒有問題、會不會產生影響。

        一般前端測試分以下幾種:

        1. 單元測試:模塊單元、函數單元、組件單元等的單元塊的測試
        2. 集成測試:接口依賴(ajax)、I/O 依賴、環境依賴(localStorage、IndexedDB)等的上下文的集成測試
        3. 樣式測試:對樣式的測試
        4. E2E 測試:端到端測試,也就是在實際生產環境測試整個應用

        一般會用到下面的一些工具:

        另外,可以參考 聊聊前端開發的測試。

        12. 構建

        一般單頁面應用的構建會有 npm run build 的命令來構建項目,然后會輸出一個 html 文件,一些 js/css/images ... 文件,然后把這些文件部署到服務器就可以了。

        多頁面應用的構建要復雜一些,因為是多入口的,所以一般會封裝構建工具,然后通過參數傳入多個入口:

        npm run build -- page1 page2 dir1/* dir2/all --env test/prod
        • page1, page2 確定構建哪些頁面;dir1/*, dir2/all 某個目錄下所有的頁面;all, * 整個項目所有的頁面
        • 有時候可能還會針對不同的服務器環境(比如測試機、正式機)做出不同的構建,可以在后面加參數
        • -- 用來分割 npm 本身的參數與腳本參數,參考 npm - run-script 了解詳情

        多頁面應用會導出多個 html 文件,需要注意這些導出的 html 不要相沖突了。

        當然,也可以用一些已經封裝好的工具,如 lila。

        13. 部署

        在構建好項目之后,就可以部署到服務器了。

        傳統的方式,可以用 ftp, sftp 等工具,手動傳到服務器,但這種方式比較笨拙,不夠自動化。

        自動化的,可以用一些工具部署到服務器,如 gulp、gulp-ssh,當然也可以用一些封裝的工具,如 md-sync、lila

        md-sync 為例:

        npm install md-sync --save-dev

        md-sync.config.js 配置文件:

        module.exports = [
          {
            src: './build/**/*',
            remotePath: 'remotePath',
            server: {
              ignoreErrors: true,
              sshConfig: {
                host: 'host',
                username: 'username',
                password: 'password'
              }
            },
          },
          {
            src: './build/**/*.html',
            remotePath: 'remotePath2',
            server: {
              ignoreErrors: true,
              sshConfig: {
                host: 'host',
                username: 'username',
                password: 'password'
              }
            },
          },
        ];

        package.jsonscripts 配置好命令:

        "scripts": {
          "deploy": "md-sync"
        }
        npm run deploy

        另外,一般大型項目會使用持續集成 + shell 命令(如 rsync)部署。

        14. 持續集成測試、構建、部署

        一般大型工程的的構建與測試都會花很長的時間,在本地做這些事情的話就不太實際,這就需要做持續集成測試、構建、部署了。

        持續集成工具用的比較多的:

        jenkins 是通用型的工具,可以與 github、bitbucket、gitlab 等代碼托管服務配合使用,優點是功能強大、插件多、社區活躍,但缺點是配置復雜、使用難度較高。

        gitlab cigitlab 內部自帶的持續集成功能,優點是使用簡單、配置簡單,但缺點是不及 jenkins 功能強大、綁定 gitlab 才能使用。

        gitlab 為例(任務定義在 .gitlab-ci.yml 中):

        stages:
          - install
          - test
          - build
          - deploy
        
        # 安裝依賴
        install:
          stage: install
          only:
            - dev
            - master
          script:
            - npm install
        
        # 運行測試用例
        test:
          stage: test
          only:
            - dev
            - master
          script:
            - npm run test
        
        # 編譯
        build:
          stage: build
          only:
            - dev
            - master
          script:
            - npm run clean
            - npm run build
        
        # 部署服務器
        deploy:
          stage: deploy
          only:
            - dev
            - master
          script:
            - npm run deploy

        以上配置表示只要在 devmaster 分支有代碼推送,就會進行持續集成,依次運行:

        • npm install
        • npm run test
        • npm run clean
        • npm run build
        • npm run deploy

        最終完成部署。如果中間某個命令失敗了,將停止接下的命令的運行,并將錯誤報告給你。

        這些操作都在遠程機器上完成。

        =================================================

        到這里為止,基本上完成了一個項目的搭建、編寫、構建。

        15. 清理服務器上過期文件

        現在前端的項目基本上都會用 webpack 打包代碼,并且文件名(html 文件除外)都是 hash 化的,如果需要清除過期的文件而又不想把服務器上文件全部刪掉然后重新構建、部署,可以使用 sclean 來清除過期文件。

        16. 收集前端錯誤反饋

        當用戶在用線上的程序時,怎么知道有沒有出 bug;如果出 bug 了,報的是什么錯;如果是 js 報錯,怎么知道是那一行運行出了錯?

        所以,在程序運行時捕捉 js 腳本錯誤,并上報到服務器,是非常有必要的。

        這里就要用到 window.onerror 了:

        window.onerror = (errorMessage, scriptURI, lineNumber, columnNumber, errorObj) => {
          const data = {
            title: document.getElementsByTagName('title')[0].innerText,
            errorMessage,
            scriptURI,
            lineNumber,
            columnNumber,
            detailMessage: (errorObj && errorObj.message) || '',
            stack: (errorObj && errorObj.stack) || '',
            userAgent: window.navigator.userAgent,
            locationHref: window.location.href,
            cookie: window.document.cookie,
          };
        
          post('url', data); // 上報到服務器
        };

        線上的 js 腳本都是壓縮過的,需要用 sourcemap 文件與 source-map 查看原始的報錯堆棧信息,可以參考 細說 js 壓縮、sourcemap、通過 sourcemap 查找原始報錯信息 了解詳細信息。

        參考:

        后續

        更多博客,查看 https://github.com/senntyou/blogs

        作者:深予之 (@senntyou)

        版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

        查看原文

        認證與成就

        • 獲得 4662 次點贊
        • 獲得 20 枚徽章 獲得 0 枚金徽章, 獲得 0 枚銀徽章, 獲得 20 枚銅徽章

        擅長技能
        編輯

        開源項目 & 著作
        編輯

        • lila

          多入口、基于任務的構建工具封裝器,webpack、gulp、rollup ...

        • see-ajax

          對 ajax 的封裝

        • see-fetch

          對 window.fetch 的封裝

        • diary

          用 react-native + expo 開發的日記 APP

        • image-viewer

          用 electron 開發的圖片瀏覽桌面軟件

        • sclean

          清除服務器端過期的 hash 文件

        注冊于 2018-04-25
        個人主頁被 12k 人瀏覽

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