<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)2021年,使用vite構(gòu)建React !

          共 4256字,需瀏覽 9分鐘

           ·

          2021-01-13 17:06

          寫(xiě)在開(kāi)頭

          • 由于 vite這個(gè)構(gòu)建工具被用在了vue3上門(mén),而且它的構(gòu)建思路我覺(jué)得優(yōu)于webpack,底層也是使用了esbuild,性能上更優(yōu)
          • 那么為了照顧一些小伙伴之前沒(méi)有學(xué)習(xí)過(guò)vite的,我們先來(lái)看看什么是vite

          什么是vite

          • Vite,一個(gè)基于瀏覽器原生 ES imports 的開(kāi)發(fā)服務(wù)器。利用瀏覽器去解析 imports,在服務(wù)器端按需編譯返回,完全跳過(guò)了打包這個(gè)概念,服務(wù)器隨起隨用,支持熱更新,而且熱更新的速度不會(huì)隨著模塊增多而變慢。針對(duì)生產(chǎn)環(huán)境則可以把同一份代碼用 rollup 打包
          • vite的天然優(yōu)勢(shì):
            • 快速冷啟動(dòng)服務(wù)器
            • 即時(shí)熱模塊更換(HMR)
            • 真正的按需編譯

          vite工作原理

          • 當(dāng)聲明一個(gè) script 標(biāo)簽類(lèi)型為 module 時(shí)
          如:??
          • 瀏覽器就會(huì)像服務(wù)器發(fā)起一個(gè)GET http://localhost:3000/src/main.js請(qǐng)求main.js文件:
          //?/src/main.js:
          import?{?createApp?}?from?'vue'
          import?App?from?'./App.vue'

          createApp(App).mount('#app')
          • 瀏覽器請(qǐng)求到了main.js文件,檢測(cè)到內(nèi)部含有import引入的包,又會(huì)對(duì)其內(nèi)部的 import 引用發(fā)起 HTTP 請(qǐng)求獲取模塊的內(nèi)容文件
          • 如:GET http://localhost:3000/@modules/vue.js
          • 如:GET http://localhost:3000/src/App.vue
          • 其Vite 的主要功能就是通過(guò)劫持瀏覽器的這些請(qǐng)求,并在后端進(jìn)行相應(yīng)的處理將項(xiàng)目中使用的文件通過(guò)簡(jiǎn)單的分解與整合,然后再返回給瀏覽器渲染頁(yè)面,vite整個(gè)過(guò)程中沒(méi)有對(duì)文件進(jìn)行打包編譯,所以其運(yùn)行速度比原始的webpack開(kāi)發(fā)編譯速度快出許多

          簡(jiǎn)單實(shí)現(xiàn)vite

          • 由于代碼量有一些大,我就不自己去寫(xiě)了,直接拿了別人的代碼過(guò)來(lái),原文地址是:

          https://juejin.cn/post/6898116372887240712

          • 首先是koa啟動(dòng)監(jiān)聽(tīng)端口,用于訪問(wèn)熱更新服務(wù)
          function?createServer()?{
          ?let?app?=?new?Koa()
          ?const?context?=?{?????//?直接創(chuàng)建一個(gè)上下文?來(lái)給不同的插件共享功能
          ??app,
          ??root:?process.cwd()?//執(zhí)行node命令的那個(gè)命令路徑
          ?}
          ?//?運(yùn)行koa中間件(就是我們的vite插件)
          ?resolvePlugin.forEach(plugin?=>?plugin(context))
          ?return?app
          }
          createServer().listen(4000,?()?=>?{
          })
          • 編寫(xiě)對(duì)應(yīng)插件處理
          • 首先處理模塊的引用,因?yàn)闉g覽器只有相對(duì)路徑和絕對(duì)路徑

          這里readBody其實(shí)就是一個(gè)讀取文件流的方法,封裝過(guò)而已,看成普通的讀取流方法即可

          koa中間件處理
          • 首先處理重寫(xiě)路徑,因?yàn)闉g覽器只有絕對(duì)路徑和相對(duì)路徑

          ??app.use(async?(ctx,?next)?=>?{
          ???await?next();?//?靜態(tài)服務(wù)
          ???//?默認(rèn)會(huì)先執(zhí)行?靜態(tài)服務(wù)中間件?會(huì)將結(jié)果放到?ctx.body
          ???//?需要將流轉(zhuǎn)換成字符串?,?只需要處理js中的引用問(wèn)題
          ???if?(ctx.body?&&?ctx.response.is('js'))?{
          ????let?r?=?await?readBody(ctx.body);?//?vue?=>?/@modules
          ????const?result?=?rewriteImports(r);
          ????ctx.body?=?result;
          ???}
          ??})
          ?},
          • 重寫(xiě)完了路徑后,需要攔截.vue文件和帶@module(重寫(xiě)路徑之前就是node_modules里面的文件)

          ?//?2.?攔截含有/@modules/vue的請(qǐng)求,?去node_modules引入對(duì)應(yīng)的模塊并返回
          ?({?app,?root?})?=>?{
          ??const?reg?=?/^\/@modules\//
          ??app.use(async?(ctx,?next)?=>?{
          ???//?如果沒(méi)有匹配到?/@modules/vue?就往下執(zhí)行即可
          ???if?(!reg.test(ctx.path))?{
          ????return?next();
          ???}
          ???const?id?=?ctx.path.replace(reg,?'');

          ???let?mapping?=?{
          ????vue:?path.resolve(root,?'node_modules',?'@vue/runtime-dom/dist/runtime-dom.esm-browser.js'),
          ???}
          ???const?content?=?await?fs.readFile(mapping[id],?'utf8');
          ???ctx.type?=?'js';?//?返回的文件是js
          ???ctx.body?=?content;
          ??})
          ?},

          • 當(dāng)解析處理完路徑后,我們需要解析vue的模板文件,(如果是react的jsx代碼,同理)
          ?//?3.?解析.vue文件
          ?({?app,?root?})?=>?{
          ??app.use(async?(ctx,?next)?=>?{
          ???if?(!ctx.path.endsWith('.vue'))?{
          ????return?next();
          ???}
          ???const?filePath?=?path.join(root,?ctx.path);
          ???const?content?=?await?fs.readFile(filePath,?'utf8');
          ???//?引入.vue文件解析模板
          ???const?{?compileTemplate,?parse?}?=?require(path.resolve(root,?'node_modules',?'@vue/compiler-sfc/dist/compiler-sfc.cjs'))
          ???let?{?descriptor?}?=?parse(content);
          ???if?(!ctx.query.type)?{
          ????//App.vue
          ????let?code?=?''
          ????if?(descriptor.script)?{
          ?????let?content?=?descriptor.script.content;
          ?????code?+=?content.replace(/((?:^|\n|;)\s*)export?default/,?'$1const?__script=');
          ????}
          ????if?(descriptor.template)?{
          ?????const?requestPath?=?ctx.path?+?`?type=template`;
          ?????code?+=?`\nimport?{?render?as?__render?}?from?"${requestPath}"`;
          ?????code?+=?`\n__script.render?=?__render`
          ????}
          ????code?+=?`\nexport?default?__script`
          ????ctx.type?=?'js';
          ????ctx.body?=?code
          ???}
          ???if?(ctx.query.type?==?'template')?{
          ????ctx.type?=?'js';
          ????let?content?=?descriptor.template.content
          ????const?{?code?}?=?compileTemplate({?source:?content?});?//?將app.vue中的模板?轉(zhuǎn)換成render函數(shù)
          ????ctx.body?=?code;
          ???}
          ??})
          ?},

          ?//?4.?靜態(tài)服務(wù)插件?實(shí)現(xiàn)可以返回文件的功能
          ?({?app,?root?})?=>?{
          ??app.use(static(root))
          ??app.use(static(path.resolve(root,?'public')))
          ?}
          ]

          function?createServer()?{
          ?let?app?=?new?Koa()
          ?const?context?=?{?????//?直接創(chuàng)建一個(gè)上下文?來(lái)給不同的插件共享功能
          ??app,
          ??root:?process.cwd()?//?C:\Users\...\my-vite-vue3
          ?}

          ?//?運(yùn)行中間件
          ?resolvePlugin.forEach(plugin?=>?plugin(context))

          ?return?app
          }

          • 下面是兩個(gè)工具函數(shù):一個(gè)是流的讀取,一個(gè)是重寫(xiě)路徑的函數(shù)
          //讀取body方法
          async?function?readBody(stream)?{
          ?if?(stream?instanceof?Readable)?{
          ??return?new?Promise((resolve)?=>?{
          ???let?res?=?''
          ???stream.on('data',?function?(chunk)?{
          ????res?+=?chunk
          ???});
          ???stream.on('end',?function?()?{
          ????resolve(res)
          ???})
          ??})
          ?}?else?{
          ??return?stream;
          ?}
          }

          • 重寫(xiě)路徑中間件
          const?resolvePlugin?=?[
          ?//?1.?重寫(xiě)引入模塊路徑前面加上/@modules/vue,?重寫(xiě)后瀏覽器會(huì)再次發(fā)送請(qǐng)求
          ?({?app,?root?})?=>?{
          ??function?rewriteImports(source)?{
          ???let?imports?=?parse(source)[0];
          ???let?ms?=?new?MagicString(source);
          ???if?(imports.length?>?0)?{
          ????for?(let?i?=?0;?i??????let?{?s,?e?}?=?imports[i];
          ?????let?id?=?source.slice(s,?e);?//?應(yīng)用的標(biāo)識(shí)?vue??./App.vue
          ?????//?不是./?或者?/
          ?????if?(/^[^\/\.]/.test(id))?{
          ??????id?=?`/@modules/${id}`;
          ??????ms.overwrite(s,?e,?id)
          ?????}
          ????}
          ???}
          ???return?ms.toString();
          ??}
          ??
          這樣一個(gè)簡(jiǎn)單的vite就完成了

          開(kāi)始在react中使用

          • vite算是一個(gè)新的技術(shù),而且在國(guó)內(nèi)目前不夠流行,為了避免踩坑,我們直接采用官方推薦的模板生成
          npm?init?vite-app?--template?react
          • 生成模板完成后,執(zhí)行命令啟動(dòng)項(xiàng)目
          yarn
          yarn?dev
          • 這樣一個(gè)react的項(xiàng)目就搭建好了,默認(rèn)使用的是17.0.0版本的react,這樣createElement方法再也不用從react里面導(dǎo)出了,我想這樣jsx風(fēng)格代碼也會(huì)更容易被遷移到其他框架項(xiàng)目中
          ??"dependencies":?{
          ????"react":?"^17.0.0",
          ????"react-dom":?"^17.0.0"
          ??},
          ??"devDependencies":?{
          ????"vite":?"^1.0.0-rc.13",
          ????"vite-plugin-react":?"^4.0.0"
          ??}
          • 這個(gè)模板生成的是自帶熱更新的,相對(duì)比較簡(jiǎn)單,如果是有特殊需求,可以使用更多的plugin,在vite.config.js中設(shè)置
          • 默認(rèn)的配置
          //?@ts-check
          import?reactPlugin?from?'vite-plugin-react'

          /**
          ?*?@type?{?import('vite').UserConfig?}
          ?*/
          const?config?=?{
          ??jsx:?'react',
          ??plugins:?[reactPlugin]
          }

          export?default?config

          寫(xiě)在最后

          • 本文更多是在講vite的實(shí)現(xiàn)原理,目前我還沒(méi)有把它使用在生產(chǎn)環(huán)境中
          • 在我看來(lái),vite如果生態(tài)能發(fā)展起來(lái),可能我們就用不到wepback6這個(gè)版本了(當(dāng)然未來(lái)不可猜測(cè))
          • 通過(guò)閱讀本文,你肯定能清楚了解vite的原理和react構(gòu)建使用了,感覺(jué)不錯(cuò)的話,幫我點(diǎn)個(gè)贊/在看,關(guān)注一下【前端巔峰】公眾號(hào)吧
          參考資料:
          • https://juejin.cn/post/6898116372887240712

          • https://juejin.cn/post/6844904146915573773

          • https://github.com/vitejs/vite


          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  精品福利在线 | 综合网五月天 | 777无码 | 午夜免费无码 | 黄色的艹逼视频网站 |