webpack學習記錄

內容

  1. 介紹、安裝及經常使用命令
  2. webpack基本配置說明
  3. webpack配置
  4. 優化
  5. 原理
  6. 手寫webpack插件
  7. 手寫loader
  8. 總結

介紹、安裝及經常使用命令

什麼是webpack

自從出現模塊化之後,你們能夠將本來一坨代碼分離到個個模塊中,可是由此引起了一個問題。每一個 JS 文件都須要從服務器去拿,由此會致使加載速度變慢。Webpack 最主要的目的就是爲了解決這個問題,將全部小文件打包成一個或多個大文件,官網的圖片很好的詮釋了這個事情,除此以外,Webpack 也是一個能讓你使用各類前端新技術的工具。css

image

webpack一共經歷了4個版本,分別是一、二、三、4,最新版本是4,如下案例是基於4實踐的html

webpack特色

  1. 專一於處理模塊化的項目,能作到開箱即用、一步到位;
  2. 可經過Plugin擴展,完整好用又不失靈活;
  3. 使用場景不侷限於web開發;
  4. 社區龐大活躍,常常引入緊跟時代發展的新特性,能爲大多數場景找到本身有的開源擴展;
  5. 良好的開發體驗;

——自來《深刻淺出webpack》前端

webpack安裝

全局安裝webpackvue

npm install -g webpack
複製代碼

局部安裝webpacknode

npm install --save-dev webpack
複製代碼

經常使用命令

webpack -p 執行一次生成環境的編譯(壓縮)
webpack --watch 在開發時持續監控增量編譯(很快)
webpack -d 讓他生成SourceMaps
webpack --progress 顯示編譯進度
webpack --colors 顯示靜態資源的顏色
webpack --sort-modules-by, --sort-chunks-by, --sort-assets-by 將modules/chunks/assets進行列表排序
webpack --display-chunks 展現編譯後的分塊
webpack --display-reasons 顯示更多引用模塊緣由
webapck --display-error-details 顯示更多報錯信息

複製代碼

最簡單配置

webpack相關參數介紹

1. entry

配置模塊的入口jquery

Entry參數說明,類型能夠是如下三種中的一種或者相互組合webpack

類型 例子 含義
string './app/entry' 入口模塊的文件路徑,能夠相對路徑
array ['./app/entry1','./app/entry2'] 入口模塊的文件路徑,能夠是相對路徑
object {a:['./app/entry-a'],b:['./app/entry-b1','./app/entry-b2']} 配置多個入口,每一個入口生成一個chunk
2. output

配置如何輸出最終想要的代碼git

output是一個object,裏面包含一系列配置項

output.filename:'[name].js'  //輸出文件的名稱
也可使用hash、chunkhash、contenthash來命名,關於hash、chunkhash、contenthash區別能夠參考https://www.cnblogs.com/tugenhua0707/p/9615822.html#_labe1_2

output.chunkFilename://chunkFilename 和上面的 filename 很是相似,但 chunkFilename 只用於指定在運行過程當中生成的 Chunk 在輸出時的文件名稱,vue按需加載就可使用這個參數

output.path:path .resolve( dirname, ’ dist [hash]’) //path 配置輸出文件存放在本地的目錄
output.publicPath:'https://cdn.example.com/assets/' //配置發佈到線上資源的 URL 前綴
output.libraryTarget://配置以何種方式導出庫,可選值var、commonjs、commonjs二、this、window、global
output.library://配置導出庫的名稱。
output.libraryExport: 配置要導出的模塊中哪些子模塊須要被導出 

複製代碼
3. module

3.1 rulesgithub

rules 配置模塊的讀取和解析規則,一般用來配置 Loader。大體能夠經過如下方式來完成web

條件匹配:經過 test、 include、 exclude 三個配置項來選中 Loader 要應用 規則的文件。

應用規則:對選中的文件經過 use 配置項來應用 Loader,能夠只應用一個 Loader或者按照從後往前的順序應用一組 Loader,同時能夠分別向 Loader傳入參數。

重置順序:一組 Loader 的執行順序默認是從右到左執行的,經過 enforce 選項能夠將其中 一個 Loader 的執行順序放到最前或者最後 。

具體方法:

module:{
    rules:[
        {
            //命中 JavaScript 文件
            test: /\.js$/,
            //用 babel-loader 轉換 JavaScript 文件
            //?cacheDirectory 表示傳給 babel-loader 的參數,用於緩存 babel 的編譯結果,
            use : [’ babel-loader?cacheDirectory ’],
            //只命中 src 目錄裏的 JavaScript 文件,加快 Webpack 的搜索速度 
            include: path.resolve( dirname, ’ src ’)
        },
        {
            //命中scss文件
            test:/\.scss$/,
            //使用一組loader去處理scss文件
            //處理順序爲從後到前,即先交給 sass-loader處理,再將結果交給 css-loader,最後交給 style-loader
            use:['style-loader','css-loader','sass-loader'],
            //排除node_modules目錄下的文件
            exclude: path.resolve( dirname, ’ node modules ’)
        },
        {
            //對非文件採用file-loader加載
            test:/\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
            use:['file-loader']
        }
    ]
    
}
複製代碼

3.2 noParse

配置項可讓Webpack忽略對部分沒采用模塊化的文件的遞歸解析和處理,這樣作的好處能提升構建性能。

//使用正則表達式
noParse: /jquerylchartjs/
複製代碼

注意,被忽略的文件裏不該該包含 import、 require、 define 等模塊化 語句,不 然會致使在構建出的代碼中包含沒法在瀏覽器環境下執行的模塊化語句 。

3.3. parse

parser 屬性能夠更細粒度地配置 哪些模塊語法被解析、哪些不被解析。同 noParse 配置項的區別在於, parser 能夠精確到 語法層 面,而 noParse 只能控制哪些文件不被解析。

parser 的使用方法以下:

module: { 
    rules : [
        {
        test: /\.js♀/,
        use: [ ’ babel-loader ’],
        parser: {
            amd: false, //禁用AMD
            commonjs : false , //禁用 CommonJS
            system : false, //禁用 SystemJS
            harmony: false, //禁用 ES6 import/export
            requireinclude:false,   //禁用require.include
            requireEnsure: false,   //禁用require.ensure
            requireContext:false,   //禁用require.context
            browserify: false, //禁用 browserify requireJs : false, //禁用 requirejs:false
            requireJs : false, //禁用 requirejs
            }
        }
    ]
}
複製代碼
4. resolve

配置尋找模塊的規則

4.1. alias

配置項經過別名來將原導入路徑映射成一個新的導入路徑

resolve:{
    alias:{
        components:'./src/components/'
    }
}
複製代碼

4.2. mainFields

會根據 mainFields 的配 置去決定 優先採用哪份代碼,

mainFields : [’jsnext:main’,’browser’,’main’]

複製代碼

4.3. extensions

Webpack 會自動帶上後綴後去嘗試訪問文件是否存在。 resolve.extensions 用於配置在嘗試過程當中用到的後綴列表

extensions:[’.ts’,’.j5 ’,’.json’]

複製代碼
  1. plugins

配置擴展插件

plugins配置項接收一個數組,數組裏的每一項都是一個要使用 的 Plugin 的實例, Plugin 須要的參數經過構造函數傳入,mini-css-extract-plugin、clean-webpack-plugin、DllReferencePlugin、html-webpack-plugin、happyPack、webpack-parallel-uglify-plugin

const ClearWebpackPlugin = require('clean-webpack-plugin');

module.exports=[
    plugins:[
        new ClearWebpackPlugin(['dist'])
    ]
];
複製代碼
  1. DevServer

hot,開啓模塊熱替換功能後,將在不刷新整個頁面的狀況下經過用新模塊替換老模塊來作到實時預覽

inline,依賴一個注入頁面裏的代理客戶端,去接收來自 DevServer的 命令並負責刷新網頁的工做。

contentBase,配置 DevServerHTTP服務器的文件根目錄

webpack配置

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ClearWebpackPlugin = require('clean-webpack-plugin');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
const HappyPack = require('happypack');
module.exports = {
    entry: './app/index/app.js', //支持多個參數,string|array|object
    output: {
        filename: ' [name].js', //輸出文件名稱
        path: path.resolve(__dirname, './dist'), //輸出文件的目標路徑
        publicPath: '/' //構建文件的輸出目錄
    },
    devServer: { //DevServer相關的配置
        contentBase: path.join(__dirname, 'dist'),
        compress: true, //壓縮
        port: 8888, //端口號
        open: true, //第一次打開瀏覽器
        hot: true, //是否監聽
        publicPath: "/" //訪問的目錄
    },
    module: {
        rules: [{
                test: /\.js$/,
                use: [' babel-loader'],
                parser: {
                    amd: true, //禁用AMD
                    commonjs: true, //禁用 CommonJS
                    system: false, //禁用 SystemJS
                    harmony: true, //禁用 ES6 import/export
                    requireinclude: false, //禁用require.include
                    requireEnsure: false, //禁用require.ensure
                    requireContext: false, //禁用require.context
                    browserify: false, //禁用 browserify requireJs : false, //禁用 requirejs:false
                    requireJs: false, //禁用 requirejs
                }
            },
            {
                // 用正則去匹配要用該 loader 轉換的 CSS 文件
                test: /\.css$/,
                use: [{
                    loader: MiniCssExtractPlugin.loader,
                }, {
                    loader: path.resolve('./loaders.js'),
                    options: {
                        test: 1
                    }
                }, {
                    loader: 'css-loader?minimize',
                }, {
                    loader: 'postcss-loader',
                    options: {
                        ident: 'postcss',
                        plugins: [
                            require('postcss-cssnext')(),
                            require('cssnano')(),
                            require('postcss-sprites')()
                        ]
                    }
                }],
            }, {
                //圖片處理
                test: /\.(png|svg|jpg|gif)$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: '[name].[ext]'
                    }
                }]
            }
        ]
    },
    resolve: {
        modules: [
            // 模塊的查找目錄
            "node_modules",
            path.resolve(__dirname, "app")
        ],
        extensions: ['.js', '.json', '.vue', '.css'],
        alias: { //模塊別名列表
            'module': 'new-module'
        }
    },
    devtool: 'source-map', //sourcemap
    plugins: [ //插件部分
        new ClearWebpackPlugin(['dist']),
        new MiniCssExtractPlugin({
            // Options similar to the same options in webpackOptions.output
            // both options are optional
            filename: `[name]_[contenthash:8].css`,
            chunkFilename: '[name]_[contenthash:8].css'
        }),
        // 告訴webpack使用了哪些第三方庫代碼
        new DllReferencePlugin({
            // jquery 映射到json文件上去
            manifest: require('./dill/jquery.manifest.json')
        }),
        new ParallelUglifyPlugin({
            // 傳遞給 UglifyJS的參數以下:
            uglifyJS: {
                output: {
                    /*
                     是否輸出可讀性較強的代碼,即會保留空格和製表符,默認爲輸出,爲了達到更好的壓縮效果,
                     能夠設置爲false
                    */
                    beautify: false,
                    /*
                     是否保留代碼中的註釋,默認爲保留,爲了達到更好的壓縮效果,能夠設置爲false
                    */
                    comments: false
                },
                compress: {
                    /*
                     是否在UglifyJS刪除沒有用到的代碼時輸出警告信息,默認爲輸出,能夠設置爲false關閉這些做用
                     不大的警告
                    */
                    warnings: false,

                    /*
                     是否刪除代碼中全部的console語句,默認爲不刪除,開啓後,會刪除全部的console語句
                    */
                    drop_console: true,

                    /*
                     是否內嵌雖然已經定義了,可是隻用到一次的變量,好比將 var x = 1; y = x, 轉換成 y = 5, 默認爲不
                     轉換,爲了達到更好的壓縮效果,能夠設置爲false
                    */
                    collapse_vars: true,

                    /*
                     是否提取出現了屢次可是沒有定義成變量去引用的靜態值,好比將 x = 'xxx'; y = 'xxx'  轉換成
                     var a = 'xxxx'; x = a; y = a; 默認爲不轉換,爲了達到更好的壓縮效果,能夠設置爲false
                    */
                    reduce_vars: true
                }
            }
        }),
        //將js自動插入到html裏
        new HtmlWebpackPlugin({
            template: './views/index.html',
            filename: 'index.html',
        }),
        new webpack.HotModuleReplacementPlugin() //引入熱更新插件
    ]
};
複製代碼

優化

優化主要是針對打包速度跟打包大小優化,主要包含如下方面

  1. 對於 Webpack4,打包項目使用 production 模式,這樣會自動開啓代碼壓縮
  2. 使用 ES6 模塊來開啓 tree shaking,這個技術能夠移除沒有使用的代碼
  3. 優化圖片,對於小圖可使用 base64 的方式寫入文件中
  4. 按照路由拆分代碼,實現按需加載
  5. 給打包出來的文件名添加哈希,實現瀏覽器緩存文件(主要採用chunkFilename)

打包速度

  1. 減小文件搜索範圍,好比經過另名,loader的test,include & exclude
  2. resolve.module,配置webpack去哪些目錄下妙手第三方模塊
  3. resolve.aslias配置,經過另外來將原導入路徑映射成一個新的導入路徑
  4. Webpack4默認壓縮並行
  5. Happypack併發調用
threads:開啓幾個子進程去處理這一類型的文件,默認是3個,必須整數
    verbose:是否容許happypack輸出日誌,默認是true
    threadpool:表明共享進程池,即多個happypack實例都使用同一個共享進程池中的子進程去處理任務,以防止資源佔用過多
複製代碼
  1. Babel使用緩存編譯,主要loader參數後面增長cacheDirectory,關於babel編譯原理
  2. 使用DllPlugin和DllReferencePlugin,這兩個跟CommonsChunkPlugin(webpack3中的,webpack4中使用SplitChunksPlugin)有一些區別,主要區別以下
  • CommonsChunkPlugin 插件每次打包的時候仍是會去處理一些第三方依賴庫,只是它能把第三方庫文件和咱們的代碼分開掉,生成一個獨立的js文件,但它不能提升打包速度。
  • DLLPlugin 它能把第三方庫代碼分離開,而且每次文件更改的時候,它只會==打包該項目自身==的代碼。因此打包速度會更快。

介紹DllPlugin插件跟DllReferencePlugin插件

DllPlugin插件 : 用於打包出一個個單獨的動態連接庫文件 。 DllReferencePlugin 插件:用於在主要的配置文件中引入 DllP!ugin 插件打包好的動態連接庫文件。

具體配置以下:

webpack.dll.config.js配置

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');

module.exports = {
    // 入口文件
    entry: {
        // 項目中用到該兩個依賴庫文件
        jquery: ['./libs/jquery'],
        // echarts: ['echarts']
    },
    // 輸出文件
    output: {
        // 文件名稱
        filename: '[name].dll.js',
        // 將輸出的文件放到dist目錄下
        path: path.resolve(__dirname, 'dill'),
        /*
         存放相關的dll文件的全局變量名稱,好比對於jquery來講的話就是 _dll_jquery, 在前面加 _dll
         是爲了防止全局變量衝突。
        */
        library: '_dll_[name]'
    },
    plugins:[
        // 使用插件 DllPlugin
    new DllPlugin({
        /*
         該插件的name屬性值須要和 output.library保存一致,該字段值,也就是輸出的 manifest.json文件中name字段的值。
         好比在jquery.manifest文件中有 name: '_dll_jquery'
        */
        name: '_dll_[name]',
  
        /* 生成manifest文件輸出的位置和文件名稱 */
        path: path.join(__dirname, 'dill', '[name].manifest.json')
      })
    ]
};
複製代碼

webpack.config.js配置

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');

{
    plugins:
    [
        // 告訴webpack使用了哪些第三方庫代碼
		new DllReferencePlugin({
			// jquery 映射到json文件上去
			manifest: require('./dill/jquery.manifest.json')
		})
	]
}
複製代碼

原理

1.webpack運行流程

1.1 webpack事件流

Webpack 就像一條生產線,要通過一系列處理流程後才能將源文件轉換成輸出結果。 這條生產線上的每一個處理流程的職責都是單一的,多個流程之間有存在依賴關係,只有完成當前處理後才能交給下一個流程去處理。 插件就像是一個插入到生產線中的一個功能,在特定的時機對生產線上的資源作處理。 Webpack 經過 Tapable 來組織這條複雜的生產線。 Webpack 在運行過程當中會廣播事件,插件只須要監聽它所關心的事件,就能加入到這條生產線中,去改變生產線的運做。 Webpack 的事件流機制保證了插件的有序性,使得整個系統擴展性很好。

1.2 webpack運行流程詳解

Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程。

  • 初始化參數:從配置文件和Shell語句中讀取與合併參數,得出最終的參數。
  • 開始編譯:用上一步獲得的參數初始化Compiler對象,加載全部配置的插件,經過執行對象的run方法開始執行編譯。
  • 肯定入口:根據配置中的entry找出全部入口文件。
  • 編譯模塊:從入口文件出發,調用全部配置的loader對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟走到全部入口依賴的文件都通過了本步驟的處理。
  • 完成模塊編譯:在通過第4步使用loader翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容及它們之間的依賴關係。
  • 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的Chunk,再將每一個Chunk轉換成一個單獨的文件加入輸出列表中,這是能夠修改輸出內容的最後機會。
  • 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,將文件的內容寫入文件系統中。

流程圖:

2.3 抽象語法樹(AST)

在計算機科學中,抽象語法樹(Abstract Syntax Tree,AST),或簡稱語法樹(Syntax tree),是源代碼語法結構的一種抽象表示。它以樹狀的形式表現編程語言的語法結構,樹上的每一個節點都表示源代碼中的一種結構。之因此說語法是「抽象」的,是由於這裏的語法並不會表示出真實語法中出現的每一個細節。好比,嵌套括號被隱含在樹的結構中,並無以節點的形式呈現;而相似於 if-condition-then 這樣的條件跳轉語句,可使用帶有兩個分支的節點來表示。

實際上一段代碼通過編譯器的詞分析、語法分析等階段以後,會生成一個樹狀結構的「抽象語法樹(AST)」,該語法樹的每個節點都對應着代碼當中不一樣含義片斷。

解釋器是將AST翻譯成目標語言並運行的工具。

手寫webpack插件

  • 調用插件apply函數傳入compiler對象
  • 經過compiler對象監聽

插件代碼

//@file: plugins/myplugin.js
class myPlugin {
    constructor(options){
        //用戶自定義配置
        this.options = options
        console.log(this.options)
    }
    apply(compiler) {
        console.log("This is my first plugin.")
    }
}

module.exports = myPlugin
複製代碼

webpack代碼

const MyPlugin = require('./plugins/myplugin-4.js')

module.exports = {
    ......,
    plugins: [
        new MyPlugin("Plugin is instancing.")
    ]
}
複製代碼

具體參數能夠參考《深刻淺出webpack》

手寫loader

  1. 得到loader的options,是經過require (’ loader-utils ’);
  2. 返回其餘結果,是調用callback
  3. 緩存加速,調用this.cacheable(false);

具體參數能夠參考《深刻淺出webpack》

loader文件

const loaderUtils = require('loader-utils');

module.exports = function (content) {
    // 獲取用戶配置的options 同步
    const options=loaderUtils.getOptions(this);
    console.log('***options***',options);
    this.callback(null,'{}'+content);
    return '{};'+content;
}
複製代碼

webpack配置文件

{
    test: /\.js$/,
    exclude: /node_modules/,
       use: {
           //這裏是個人自定義loader的存放路徑
           loader: path.resolve('./loaders/index.js'),
           options: {
              test: 1
           }
       }
}
複製代碼

總結

webpack打包配置相對比較複雜,目前介紹應該能夠滿常規需求。

參考資料:

《深刻淺出webpack》

Webpack運行機制

編寫自定義webpack plugin

編寫自定義webpack loader

歡迎關注github

相關文章
相關標籤/搜索