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

          手摸手,實(shí)現(xiàn)一個專屬于你的babel-loader

          共 5382字,需瀏覽 11分鐘

           ·

          2020-08-01 04:01

          藍(lán)術(shù)優(yōu)關(guān)!

          寫一個簡單的 babel-loader

          這里所有的代碼都在github上,地址 https://github.com/lihongxun945/my-babel-loader。

          這里以 babel-loader 為例,看我們?nèi)绾螌懸粋€自己的loader。首先,我們參考這篇官方教程,雖然寫的很粗略,但是我們可以學(xué)會寫一個簡單的loader。

          最簡單的loader是一個什么都不做,原樣返回JS代碼的 loader,像這樣:

          module.exports?=?function?(source)?{
          ??return?source
          }

          但是我們的 babel-loader 顯然需要調(diào)用 babel 來編譯代碼,我們查一下 babel-core 文檔,可以調(diào)用 babel.transform API來編譯代碼。再加上一些 presets 的設(shè)置,我們可以把上面的代碼做一下改造如下:

          var?babel?=?require("babel-core")
          module.exports?=?function?(source)?{
          ??var?babelOptions?=?{
          ????presets:?['env']
          ??}
          ??var?result?=?babel.transform(source,?babelOptions)
          ??return?result.code
          }

          關(guān)于 Babel 的API用法不在這里詳細(xì)解釋,有興趣的可以直接去看官方的文檔。我們這里做了一個很簡單的轉(zhuǎn)換,就是把 接收到的 source 源碼,用 babel 編譯一下,然后返回編譯后的代碼。

          那么問題來了,我們要如何指定使用自己的loader呢,可以參考下面這種寫法,直接在 webpack config 文件里面加一個 resolveLoader 的配置即可,我們這里把 bable-loader 指定為自己寫的。

          ??resolveLoader:?{
          ????alias:?{
          ??????"babel-loader":?resolve('./build/babel-loader.js')
          ????}
          ??},

          然后我們就可以運(yùn)行 npm run dev 編譯自己的JS代碼,比如我寫了這么幾行代碼:

          class?People?{
          ??constructor?(name)?{
          ????this.name?=?name
          ??}

          ??sayName?()?{
          ????console.log(`Hello?there,?I'm?${this.name}`)
          ??}
          }

          const?lily?=?new?People('Lily')
          lily.sayName()

          經(jīng)過我們的 babel-loader 編譯后,最終輸出的代碼是這樣的:

          var?People?=?function?()?{
          ??function?People(name)?{
          ????_classCallCheck(this,?People);

          ????this.name?=?name;
          ??}

          ??_createClass(People,?[{
          ????key:?'sayName',
          ????value:?function?sayName()?{
          ??????console.log('Hello?there,?I\'m?'?+?this.name);
          ????}
          ??}]);

          ??return?People;
          }();

          var?lily?=?new?People('Lily');
          lily.sayName();

          可以看到其中 class , 字符串模板,const 等都被 babel 編譯過。

          添加sourcemap

          上面的簡單代碼并沒有實(shí)現(xiàn) sourcemap 功能,如果需要支持 sourcemap,顯然需要把 babel-core 產(chǎn)生的 sourcemap 傳給 webpack。之前因?yàn)橹环祷鼐幾g后的代碼,所以我們直接返回了字符串,如果需要同時返回編譯的代碼和sourcemap,我們需要這個接口 this.callback,官方文檔上是這么說的:

          this.callback(
          ??err:?Error?|?null,
          ??content:?string?|?Buffer,
          ??sourceMap?:?SourceMap,
          ??meta?:?any
          );

          那么我們查一下babel的文檔之后,很簡單就能獲取 sourcemap 了,其實(shí)就是 result.map,改動后的代碼如下:

          var?babel?=?require("babel-core")

          module.exports?=?function?(source,?inputSourceMap)?{
          ??var?babelOptions?=?{
          ????presets:?['env'],
          ????inputSourceMap:?inputSourceMap,
          ????sourceMaps:?true
          ??}
          ??var?result?=?babel.transform(source,?babelOptions)
          ??this.callback(null,?result.code,?result.map)
          }

          這里 sourceMaps: true 是告訴 babel 要生成 sourcemap,因?yàn)槟J(rèn)情況下它是不會生成的。然后刷新頁面應(yīng)該就能看到sourcemap了。

          然而,根據(jù)你的webpack配置不同,很可能并看不到~~~這是因?yàn)?sourcemap 其實(shí)是要交給 webpack 來管理的,我們只是把 sourcemap 傳給了 webpack,而它在最終編譯出的代碼中到底要不要顯示是 webpack 自己的配置決定的。那么我們在 webpack.config.js 中添加一行代碼打開sourcemap功能即可:

          devtool:?'eval-source-map'

          這樣再刷新頁面就能看到sourcemap了,確實(shí)可以,然而 sourcemap 的文件名怎么是 unknown?我們在創(chuàng)建 sourcemap的時候需要指定一下文件名,不然確實(shí)會出現(xiàn) unknown 的問題。

          那么問題又來了,怎么獲取文件名呢?

          可以通過 this.request 來獲取當(dāng)前的文件名,比如這個示例中的 ?this.request 是:

          ~/github/my-webpack-loader/build/babel-loader.js!~/github/my-webpack-loader/src/main.js

          其中 ~ 是被我省略的絕對路徑

          可以看出 this.request 就是一個加載文件的請求,包含了兩部分,通過 ! 分割,前一部分是 對應(yīng)的 loader,后一部分是文件的路徑。我們一行代碼就可以把文件名提取出來:

          this.request.split('!')[1].split('/').pop()

          然后在 babel.transform 的配置中增加一行:

          filename:?this.request.split('!')[1].split('/').pop()

          再刷新頁面就可以看到正常的 sourcemap 了。

          模塊

          我們現(xiàn)在已經(jīng)支持 編譯代碼 和 sourcemap,下面我們要支持 modules,我們把 main.js 代碼拆成兩部分:

          people.js

          export?default?class?People?{
          ??constructor?(name)?{
          ????this.name?=?name
          ??}

          ??sayName?()?{
          ????console.log(`Hello?there,?I'm?${this.name}`)
          ??}
          }

          main.js

          import?People?from?'./people'

          const?lily?=?new?People('Lily')
          lily.sayName()

          那么我們需要怎么做處理呢?對JS文件來說,我們最好的方式是不作任何特殊處理。上面的代碼其實(shí)已經(jīng)可以正常打包模塊了。那么是怎么做到的呢?

          因?yàn)镴S在webpack中是一等公民,webpack把所有的資源都當(dāng)做JS來加載。webpack默認(rèn)支持常見的AMD,CMD,ES6,nodejs 等常見的模塊加載方式,并且會自動做 bundle(打包)和 tree-shaking。反而,babel 只是把 ES6 模塊編譯成了 nodejs 模塊,它并不會做bundle。

          比如我們的 people.js,經(jīng)過 babel-loader 編譯出來的代碼是這樣的:

          //?省略幾個工具方法。。。
          var?People?=?function?()?{
          ??function?People(name)?{
          ????_classCallCheck(this,?People);

          ????this.name?=?name;
          ??}

          ??_createClass(People,?[{
          ????key:?"sayName",
          ????value:?function?sayName()?{
          ??????console.log("Hello?there,?I'm?"?+?this.name);
          ????}
          ??}]);

          ??return?People;
          }();

          exports.default?=?People;

          webpack 會識別 exports.default 語法,并且最終把兩個文件合并一個大的文件。反而,如果我們在 babel-loader 中做了打包,會導(dǎo)致 webpack 無法做 tree-shaking 優(yōu)化。

          這也是webpack 的各種 JS loader,如 vue-loader, jsx-loader 等的共同做法,即把 modules bundle 交給 webpack 處理,讓webpack做最大程度的優(yōu)化。

          當(dāng)然,這種做法僅僅對 JS 有效,因?yàn)镴S是webpack的一等公民,webpack內(nèi)置了對JS 模塊的完整支持,而其他的文件,比如 css, html 等,我們都需要把他們轉(zhuǎn)成JS然后交給webpack。這也是為什么 webpack 中有 style-loader, url-loader 卻沒有一個 js-loader 的原因。

          我們的 babel-loader 代碼就已經(jīng)寫完了。其實(shí)總共就10行代碼,最終完整代碼如下所示:

          var?babel?=?require("babel-core")

          module.exports?=?function?(source,?inputSourceMap)?{
          ??var?babelOptions?=?{
          ????presets:?['env'],
          ????inputSourceMap:?inputSourceMap,
          ????filename:?this.request.split('!')[1].split('/').pop(),
          ????sourceMaps:?true
          ??}
          ??var?result?=?babel.transform(source,?babelOptions)
          ??this.callback(null,?result.code,?result.map)
          }

          那么我們?nèi)タ匆幌鹿俜降?babel-loader 是如何實(shí)現(xiàn)的。顯然他的代碼比我們的代碼多很多,他寫了那么多,其實(shí)主要是增加了 Cache,以及增加了對異常的處理。有興趣的可以自己去研究一下。

          關(guān)于webpack的模塊加載機(jī)制,我們后續(xù)再詳細(xì)解讀。

          如何編譯 JSX

          如果我們是使用React寫的組件,那么同樣可以通過 babel-loader 來編譯。關(guān)于如何編譯JSX,babel官網(wǎng)這里做了很詳細(xì)的文檔 transform-react-jsx

          簡單來說,就是 babel-core 本身雖然不支持 jsx,會報語法錯誤,但是我們可以通過加載一個插件就能支持 jsx用法如下:

          require("babel-core").transform("code",?{
          ??plugins:?["transform-react-jsx"]
          });

          那么我們只要在 上面我們寫的 bable-loader 的代碼中加入一行 plugins: ["transform-react-jsx"] 就可以支持 jsx 的編譯了,是不是很簡單。

          react 因?yàn)橛玫腏SX,而jsx 因?yàn)槿烤幾g成了JS 所以它的loader很簡單。但是 Vue 的組件并不是可以直接就全部編譯成JS,而是包括了 html,JS,CSS三部分,所以 vue-loader 相對來說就復(fù)雜很多了。

          如果你喜歡探討技術(shù),或者對本文有任何的意見或建議,非常歡迎加魚頭微信好友一起探討,當(dāng)然,魚頭也非常希望能跟你一起聊生活,聊愛好,談天說地。魚頭的微信號是:krisChans95 也可以掃碼關(guān)注公眾號,訂閱更多精彩內(nèi)容。


          在看點(diǎn)這里
          瀏覽 34
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  老牛影视AV牛牛影视av | 一级黄色免费电影 | 日本美女骚逼被操 | 爱99区区 | 国产成人综合久久 |