<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          [項目實戰(zhàn)] Webpack to Vite, 為開發(fā)提速!

          共 9805字,需瀏覽 20分鐘

           ·

          2021-04-23 19:20

          Webpack to Vite

          背景

          最近,就 前端開發(fā)過程中的痛點及可優(yōu)化項 做了一次收集。 其中,構(gòu)建耗時、項目編譯速度慢 的字眼出現(xiàn)了好幾次。

          隨著業(yè)務(wù)的快速發(fā)展,我們很多項目的體積也快速膨脹。隨之而來的, 就是打包變慢等問題。

          提升研發(fā)效率,是技術(shù)人永恒的追求。

          我們項目也有啟動慢的問題,同事也提到過幾次。剛好我之前也做過類似的探索和優(yōu)化, 于是就借這個機會,改造一下項目, 解決啟動耗時的問題

          于昨天下午(2021.4.7 23:00), 成功嵌入 Vite, 項目啟動時間由約 190s => 20s, 熱更新時間縮短為 2s

          中間踩了一些坑, 好在最后爬出來了, 相關(guān)技術(shù)要點都會在下文中呈現(xiàn)。


          FBI Warning:以下文字,只是我結(jié)合自己的實際項目, 總結(jié)出來的一些淺薄的經(jīng)驗, 如有錯誤,歡迎指正 :)

          今天的主要內(nèi)容:

          • 為什么 Vite 啟動這么快
          • 我的項目如何植入 Vite
          • 改造過程中遇到的問題以及解決方式
          • 關(guān)于 Vite 開發(fā)、打包上線的一些思考
          • 相關(guān)代碼和結(jié)論

          正文

          為什么 Vite 啟動這么快

          底層實現(xiàn)上, Vite 是基于 esbuild 預(yù)構(gòu)建依賴的。

          esbuild 使用 go 編寫,并且比以 js 編寫的打包器預(yù)構(gòu)建依賴, 快 10 - 100 倍。

          因為 js 跟 go 相比實在是太慢了,js 的一般操作都是毫秒計,go 則是納秒。

          另外, 兩者的啟動方式也有所差異。

          webpack 啟動方式

          image.png

          Vite 啟動方式

          image.png

          Webpack 會先打包,然后啟動開發(fā)服務(wù)器,請求服務(wù)器時直接給予打包結(jié)果。

          而 Vite 是直接啟動開發(fā)服務(wù)器,請求哪個模塊再對該模塊進行實時編譯

          由于現(xiàn)代瀏覽器本身就支持 ES Module,會自動向依賴的 Module 發(fā)出請求。

          Vite 充分利用了這一點,將開發(fā)環(huán)境下的模塊文件,就作為瀏覽器要執(zhí)行的文件,而不是像 W ebpack 那樣進行打包合并

          由于 Vite 在啟動的時候不需要打包,也就意味著不需要分析模塊的依賴不需要編譯。因此啟動速度非常快。當瀏覽器請求某個模塊時,再根據(jù)需要對模塊內(nèi)容進行編譯。

          這種按需動態(tài)編譯的方式,極大的縮減了編譯時間,項目越復(fù)雜、模塊越多,vite 的優(yōu)勢越明顯。

          在 HMR(熱更新)方面,當改動了一個模塊后,僅需讓瀏覽器重新請求該模塊即可,不像webpack那樣需要把該模塊的相關(guān)依賴模塊全部編譯一次,效率更高。

          從實際的開發(fā)體驗來看, 在 Vite 模式下, 開發(fā)環(huán)境可以瞬間啟動, 但是等到頁面出來, 要等一段時間。

          我的項目如何植入 Vite

          新項目

          創(chuàng)建一個 Vite 新項目就比較簡單:

          yarn create @vitejs/app
          image.png
          image.png

          生成好之后, 直接啟動就可以了:

          image.png

          已有項目

          已有項目的遷移, 稍微繁瑣一些。

          首先, 加入 Vite 的相關(guān)配置。這里我使用了一個 cli 工具:wp2vite.

          安裝好之后, 直接執(zhí)行:

          image.png

          這一步, 會自動生成 Vite 的配置文件,并引入相關(guān)的依賴。

          把依賴安裝一下, 啟動就可以了。

          如果沒有意外的話, 你會收獲一堆報錯

          恭喜你,進入開心愉快的踩坑環(huán)節(jié)。

          我在改造過程中遇到的問題

          1. alias 錯誤

          image.png

          項目代碼里配置了一些別名,vite 無法識別,所以需要在vite 里面也配置 alias:

            resolve: {
              alias: {
                '@': resolve(__dirname, 'src'),
              },
            },

          2. 無法識別 less 全局變量

          image.png

          解決辦法:

          把自定義的全局變量從外部注入即可, 直接在 vite.config.js 的 css 選項中加入:

            css: {
              preprocessorOptions: {
                less: {
                  modifyVars: {
                    hack`true;@import '${resolve('./src/vars.less')}';`,
                    ...themeVariables,
                  },
                  javascriptEnabledtrue,
                },
              },
            },

          3. Uncaught Error: Target container is not a DOM element.

          image.png

          根元素未找到。

          原因是:默認生成的 index.html 中:

          <div id="root"></div>

          id 是 root, 而邏輯中的是#app, 這里直接改成 id=app 即可。

          4. typings 文件找不到

          image.png

          typings 文件未找到

          這個錯誤, 乍一看, 一頭霧水。

          進去看一下源代碼和編譯后的代碼:

          源代碼:

          image.png

          編譯后:

          image.png
          image.png

          typings 文件這不是好好的在這嗎, 怎么就找不到?

          想了一下:Vite 不知道 typeings 文件是不需要被編譯的,需要告訴編譯器不編譯這個文件。

          最后在 TS 官方文檔里找到了答案:

          https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html

          Type-Only Imports and Export

          This feature is something most users may never have to think about; however, if you’ve hit issues under --isolatedModules, TypeScript’s transpileModule API, or Babel, this feature might be relevant.

          TypeScript 3.8 adds a new syntax for type-only imports and exports.

          import type { SomeThing } from "./some-module.js";
          export type { SomeThing };

          需要單獨引入types, 于是把代碼改為:

          image.png

          同時要注意, 如果一個文件有有多個導(dǎo)出, 也要分開引入:

          image.png

          唯一痛苦的是: 全局都需要改一遍, 體力活。

          至此,typeings 問題完美解決。

          5. 無法識別 svg

          我們在使用 svg 作為圖標組件的時候, 一般是:

          import Icon from '@ant-design/icons';
          import ErrorSvg from '@/assets/ico_error.svg';

          const ErrorIcon = (props: any) => <Icon component={ErrorSvg} />;

          // ...
          <ErrorIcon />

          瀏覽器報錯:

          image.png
          error occurred in the </src/assets/ico_error.svg> component

          很明顯的看到, 這里是把文件路徑作為組件了。

          現(xiàn)在要做的是:把這個文件路徑, 換成可以識別的組件。

          搜索一番, 找到了個插件:vite-plugin-react-svg

          加入配置:

          const reactSvgPlugin = require('vite-plugin-react-svg');

          plugins: [
            reactSvgPlugin(),
          ],
          import MyIcon from './svgs/my-icon.svg?component';

          function App({
            return (
              <div>
                <MyIcon />
              </div>

            );
          }

          需要注意的是:引入的 svg 文件需要加 ?component 作為后綴。

          看了一下源碼, 這個后綴是用來作為標識符的,

          image.png

          如果后綴匹配上是component,  就解析文件, 并緩存, 最后返回結(jié)果:

          image.png

          知道原理之后, 就需要把全部的 .svg => .svg?component

          vscode 一鍵替換就可以, 不過注意別把 node_module 里面的也替換了。

          6. global 未定義

          image.png

          global 是 Node里面的變量, 會在客戶端報錯 ?

          一層層看下去, 原來是引入的第三方包使用了global。

          看 vite 文檔里提到了 Client Types:

          image.png

          追加到 tsconfig 里面:

           "compilerOptions": {
              "types": ["node""jest""vite/client"],
           }

          然后, 并沒有什么亂用。。。

          image.png

          沒辦法, 只得祭出 window 大法。

          在入口index.tsx 里面加上:

          (window as any).global = window;

          刷新, 好了。

          image.png

          7. [未解決] 替代HtmlWebpackPlugin

          還需要注入一些外部變量, 修改入口html, favicon, title 之類。

          找到一個插件:vite-plugin-singlefile

          不過并沒有什么用。

          有了解的同學(xué)請留言賜教。

          至此, 整個 app 已經(jīng)能在本地跑起來了, build 也沒問題。

          7. 線上打包構(gòu)建時, 內(nèi)存溢出

          本地能跑起來, 打包也沒問題, 后面當然是放到線上跑一跑啦。

          立刻安排!

          memory.png

          內(nèi)存不足, 我就給你加點:

          image.png
          success.png

          搞定!

          關(guān)于 Vite 開發(fā)、打包上線的一些思考

          從實際使用來看, vite 在一些功能上還是無法完全替代 webpack。

          畢竟是后起之秀, 相關(guān)的生態(tài)還需要持續(xù)完善。

          個人認為,目前一種比較穩(wěn)妥的方式是:

          • 保留 webpack dev & build 的能力, vite 僅作為開發(fā)的輔助

          等相關(guān)工具再完善一些, 再考慮完全遷移過來。

          相關(guān)代碼和結(jié)論

          一個完整的 Vite demo

          倉庫地址:https://github.com/beMySun/react-hooks-i18n-template/tree/test-wp2vite

          image.png

          業(yè)務(wù)項目的 vite.config.js 完整配置

          import { defineConfig } from 'vite';
          import reactRefresh from '@vitejs/plugin-react-refresh';
          import legacyPlugin from '@vitejs/plugin-legacy';
          import { resolve } from 'path';

          const fs = require('fs');
          const lessToJS = require('less-vars-to-js');
          const themeVariables = lessToJS(fs.readFileSync(resolve(__dirname, './src/antd-custom.less'), 'utf8'));
          const reactSvgPlugin = require('vite-plugin-react-svg');

          // https://cn.vitejs.dev/config/
          export default defineConfig({
            base'./',
            root'./',
            resolve: {
              alias: {
                'react-native''react-native-web',
                '@': resolve(__dirname, 'src'),
              },
            },
            define: {
              'process.env.REACT_APP_IS_LOCAL''\'true\'',
              'window.__CID__'JSON.stringify(process.env.cid || 'id'),
            },
            server: {
              port8080,
              proxy: {
                '/api': {
                  target'https://stoku.test.shopee.co.id/',
                  changeOrigintrue,
                  cookieDomainRewrite: {
                    'stoku.test.shopee.co.id''localhost',
                  },
                },
              },
            },
            build: {
              target'es2015',
              minify'terser',
              manifestfalse,
              sourcemapfalse,
              outDir'build',
              rollupOptions: {},
            },
            esbuild: {},
            optimizeDeps: {},
            plugins: [
              // viteSingleFile({
              //   title: 'dynamic title', // doesn't work
              // }),
              reactSvgPlugin(),
              reactRefresh(),
              legacyPlugin({
                targets: [
                  'Android > 39',
                  'Chrome >= 60',
                  'Safari >= 10.1',
                  'iOS >= 10.3',
                  'Firefox >= 54',
                  'Edge >= 15',
                ],
              }),
              // vitePluginImp({
              //   libList: [
              //     {
              //       libName: 'antd',
              //       style: (name) => `antd/es/${name}/style`,
              //     },
              //   ],
              // }),
            ],
            css: {
              preprocessorOptions: {
                less: {
                  modifyVars: {
                    hack`true;@import '${resolve('./src/vars.less')}';`,
                    ...themeVariables,
                  },
                  javascriptEnabledtrue,
                },
              },
            },
          });

          最后

          使用 Vite 能大幅縮短項目構(gòu)建時間,提升開發(fā)效率。

          不過也要結(jié)合項目的實際情況,合理取舍。

          對于我的這個項目而言,把 Vite 作為輔助開發(fā)的一種方式,還是挺有用的。

          期待 Vite 能繼續(xù)完善,為研發(fā)提效。

          好了, 內(nèi)容大概就這么多, 希望對大家有所幫助。

          才疏學(xué)淺,如有錯誤, 歡迎指正。

          謝謝。

          END



          如果覺得這篇文章還不錯
          點擊下面卡片關(guān)注我
          來個【分享、點贊、在看】三連支持一下吧

             “分享、點贊在看” 支持一波  

          瀏覽 59
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久午夜无码鲁丝片午夜精 | 日本爱爱视频一区 | 不卡成人免费在线视频 | 美女插屄视频 | 超碰青青在线五月 |