1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 5382字,需瀏覽 11分鐘

         ·

        2020-08-01 04:01

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

        寫一個(gè)簡單的 babel-loader

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

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

        最簡單的loader是一個(gè)什么都不做,原樣返回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ì)解釋,有興趣的可以直接去看官方的文檔。我們這里做了一個(gè)很簡單的轉(zhuǎn)換,就是把 接收到的 source 源碼,用 babel 編譯一下,然后返回編譯后的代碼。

        那么問題來了,我們要如何指定使用自己的loader呢,可以參考下面這種寫法,直接在 webpack config 文件里面加一個(gè) 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后的代碼,所以我們直接返回了字符串,如果需要同時(shí)返回編譯的代碼和sourcemap,我們需要這個(gè)接口 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í)候需要指定一下文件名,不然確實(shí)會出現(xiàn) unknown 的問題。

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

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

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

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

        可以看出 this.request 就是一個(gè)加載文件的請求,包含了兩部分,通過 ! 分割,前一部分是 對應(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 編譯出來的代碼是這樣的:

        //?省略幾個(gè)工具方法。。。
        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 語法,并且最終把兩個(gè)文件合并一個(gè)大的文件。反而,如果我們在 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 卻沒有一個(gè) 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,會報(bào)語法錯誤,但是我們可以通過加載一個(gè)插件就能支持 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ī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品免费久久久久久久久久中文 | 受被np攻强迫无力反抗肉多 | 影音先锋乱伦 | 深夜福利麻豆 | 思思热在线观看视频 | 黄色免费片 | 少妇菊爆在线播放 | 做爱在线观看免费 | 尤物黄视频| 久久免费看少妇 |