從vue-loader開始理解webpack的一些設計思想

寫在前面

爲了更好的說明,咱們模仿Vue.js開發了一個相似的簡化版本的前端框架Quick Paper(文檔) 來幫助你理解一些細節。所以在開始以前,讓咱們先大體瞭解一下此項目的結構,方便後續描述。css

舒適提示:咱們推薦你在開始以前去Github上把此項目clone下來後,對照着源碼進行學習!html

目錄結構

其實你只須要關注下面四個文件夾:前端

  1. src:框架源碼;
  2. loader:相似vue-loader,用來解析.paper文件的loader;
  3. style-loader:和上面的同樣,只不過這個是用來解析樣式文件的(包括.css文件和.paper文件中的style標籤部分);
  4. loader-plug:一些輔助功能,好比校對webpack的一些配置。

框架源碼

接着,咱們對源碼src部分的目錄結構再稍微展開一下(由於咱們這裏的重點不是源碼部分,而是那些loader或plug是如何配置完成一系列解析工做的,所以源碼部分就在下面簡單的說明就點到爲止)。vue

  • core:框架對象的基礎代碼node

    • global-api:給框架對象掛載的全局方法
    • instance:框架對象webpack

      • index.js:框架對象運行入口
      • init.js:負責對象的初始化相關工做
      • lifecycle.js:負責對象的生命週期管理
      • render.js:對象的渲染啓動等方面的任務
    • observe:監聽數據改變方法(被框架對象使用)
    • vnode:虛擬DOM相關代碼(被框架對象使用)
  • module:爲框架對象擴展內置指令,組件等的地方
  • tools:一些工具方法,由於複用性和方便管理,集中寫在一塊兒
  • index.js:打包入口文件,也就是這份文件把全部的資源整合成一個完整的框架

因此從上面的代碼就能夠看出來,文件src/core/instance/index.js是對象自己,從這個文件開始開便可!git

若是有什麼不清楚的,能夠去issue給咱們留言。github

Loader和執行順序

對於咱們用於學習的項目Quick Paper而言,咱們是把代碼整合到文件.paper中去,文件結構大體以下:web

<template>
    <!-- 頁面的元素在這裏 -->
</template>
<script>
    // 邏輯控制代碼在這裏
    export default {

    };
</script>
<style>
    /* 在這裏編輯樣式代碼 */
</style>

你想,咱們使用webpack打包項目的時候,他是不可能認識.paper文件的,固然就沒法知道如何解析上面這份文件了,而開發一個loader用以解析上面的文件,就是這裏要說明的。api

loader

在說明loader以前,咱們先要看看咱們編輯的.paper是如何被咱們使用的。由於如何使用就決定了咱們須要如何解析。

和vue相似,先假設咱們有一個App.paper文件:

import App from './App.paper';
new QuickPaper({
    render:createElement => createElement(App),
    // ...
});

由於render裏面只記錄了頁面內容,但是.paper文件裏面但是記錄了頁面內容+邏輯控制+頁面樣式的。其他的內容怎麼辦?

// 導入js [邏輯控制]
import script from './${filename}?QuickPaper&type=script&lang=js&hash=${id}&';

// 導入css [頁面樣式]
import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

script.render=${code};

// [頁面內容]
export default script;

能夠看出來,頁面內容直接默認導出後給render配置項便可,別的內容由於新增了導入語句,會觸發對應的loader進行解析,也就是說,這裏其實能夠分爲兩步:

  • 第一步:對於未考慮到的內容執行新的導入語句,觸發對應的loader解析
  • 第二步:導出render須要的內容

style-loader

好比頁面樣式部分的導入語句:

import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

咱們是如何讓webpack知道這是一個樣式文件,而且是使用css仍是scss或別的loader來解析的,這屬於插件須要說明的部分。在此以前,咱們還須要先說明一下樣式loader的工做原理。

爲何樣式loader比較特殊?

根據返回值類型,能夠把loader分紅兩種:一種是返回js代碼(也就是一個模塊的代碼,有相似module.export語句)的loader,一個是不能做爲最左邊loader的其餘loader(好比返回一個CSS字符串)。

咱們來看看咱們webpack裏面是如何配置css的loader的:

{
    test: /\.css$/,
    loader: ['quick-paper/style-loader/index.js', 'css-loader', 'postcss-loader']
}

這裏的重點是css-loader,他屬於第一種,返回js代碼的loader,對於咱們自定義的'quick-paper/style-loader/index.js'而言,若是讓loader按照從右往左的順序執行,很難拿到真正的css代碼。

執行順序(loader和picth)

在說明如何解決上個問題前,咱們須要先說明一下loader的picth和執行順序。

好比上面配置的三個loader而言,執行順序分爲Pitch階段和Normal階段(能夠理解爲loader自己的行爲):

  • Pitch階段:'quick-paper/style-loader/index.js'->'css-loader'->'postcss-loader'
  • Normal階段:'postcss-loader'->'css-loader'->'quick-paper/style-loader/index.js'

有一個特色是,在Pitch階段,若是某個loader有返回值,就會中止後續執行。

舒適提示:中止執行的意思是,在其右邊的loader,包括本身都執行完畢了(Pitch階段和Normal階段都結束了),返回的值會返回給前一個loader(Normal階段)!

如何實現?

這裏,咱們就能夠藉助給'quick-paper/style-loader/index.js'設置一個有返回值的Pitch來實現。

看看代碼結構:

// quick-paper/style-loader/index.js

const loaderApi = () => { };
loaderApi.pitch = function (remainingRequest) {

    // request = ""!!../../node_modules/css-loader/dist/cjs.js!../../node_modules/postcss-loader/src/in...
    let request = loaderUtils.stringifyRequest(this, '!!' + remainingRequest)

    return `

        // 獲取真正的css內容
        var content = require(' + request + ');
        // 而後調用方法添加到頁面中生效
        require('./addStylesClient.js')(content);
    `;
};
module.exports = loaderApi;

咱們在'quick-paper/style-loader/index.js'中定義了Picth方法,在此方法裏面,返回了一個js字符串,項目運行的時候會運行這段字符串,這段字符串的意義就是調用樣式loader獲取真正的css後,運行addStylesClient.js方法使得在頁面生效。

舒適提示:關於addStylesClient.js方法請直接查看項目源碼,很容易讀懂,給樣式添加hash值讓scope生效,就是這個方法裏。

插件的做用和一些技巧

咱們這裏來解釋一下,一個.paper文件拆分之後,如何讓對應的loader來進行解析。

插件的執行時機

首先須要理解,什麼是插件?

你能夠這樣理解:若是說loader幫助webpack得到解析更多類型文件,那插件就是一個打雜工,前者有專門的分工,後者是在特殊狀況下幫助,而不是針對某個文件。

好比你能夠在每次打包前調用一個查看刪除上次打包的結果,或者在打包失敗的時候重置一些參數,或者是別的一些操做等。

如何實現?

那麼,咱們這裏須要插件幹什麼?

別忘了咱們的需求是(拿css舉例子),若是遇到:

import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

這樣的導入語句,咱們工具lang=css發現是一個樣式文件,應該交給專門解析css的loader處理,固然,咱們能夠主動修改webpack的配置:

{
    test: /type=style&lang=css/,
    loader: ['quick-paper/style-loader/index.js', 'css-loader', 'postcss-loader']
}

但是,爲了更簡單,咱們能夠經過插件,在每次打包前對loader配置進行修改(固然,也包括js等相關項),如此,便實現了。

相關文章
相關標籤/搜索