[愣錘筆記]中高級前端極速通關webpack4(圖文詳情持續更新中)

前端構建工具從grunt、glup+fis、rollup,到現在伴隨mvvm大熱的webpack也迎來了4x的版本(parcel勢頭也很猛,將來可期~)。熟練掌握webpack也成了中高級前端的加分項,甚至是必備項。任什麼時候候,無論構建工具怎麼變,其本質的構建思想是不變的,例如咱們須要在開發環境有熱更新,在生成環境須要壓縮文件,抽離公共庫等。css

本文旨在分享快速掌握webpack4的核心內容,一塊兒快速通關,爭取人人都是webpack配置工程師~~html

基礎安裝

  • cURL是一個利用URL語法在命令行下工做的文件傳輸工具
// 查看curl的命令幫助
curl --help

// 能夠利用curl安裝nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
複製代碼
  • nvm是node.js的包管理工具,地址

專門給OSX/Linux系統使用的,window可使用nvm-windows,具體查看github前端

// 將nvm命令寫入path
source ~/.bash_profile
// 查看是否安裝成功
nvm // 有輸出內容則安裝成功

// 若是寫入命令時提示no such profile
// 則在終端中進行以下操做
cd ~ // 進入用戶的home目錄
open .bash_profile // 打開配置文件
// 沒有該文件則執行以下命令建立一個該文件
touch .bash_profile
// 建立完成後,從新打開 open .bash_profile 
// 在打開的文件中鍵入以下內容並保存
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

// 最後查看安裝是否成功
nvm
// 查看本地已安裝的node
nvm list
複製代碼
  • 安裝webpack4
// webpack4是webpack和webpack-cli分離開的
// 須要分別安裝,開發使用
cnpm i webpack webpack-cli --save-dev

// 查看有沒有安裝成功
// 直接找到node_modules中的webpack查看版本
./node_modules/bin/webpack -v
複製代碼
  • npm運行webpack
// package.json增長npm命令
"scripts": {
    "build": "webpack"
}

// 經過npm命令進行webpack打包
npm run build
複製代碼

核心概念

  • webpack一切皆模塊,entry來指定webpack打包的入口;html5

  • webpack根據entry入口文件,找到對應的依賴關係,造成一個依賴圖,而後根據這個依賴圖打包成各類文件;node

  • output用來告訴webpack如何將編譯後的文件輸出到磁盤中;react

// path用來指定輸出文件的地址
// filename用來指定輸出文件的名稱,
// [name]表示佔位符,用文件的名稱來命名。
// 對於多入口文件的打包,用佔位符
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
},
複製代碼
  • webpack只支持js/json類型的文件,對於其餘webpack不支持的文件類型(如css/ts/image/less等),須要經過loader轉換成有效的模塊。webpack

  • plugin插件,用於加強webpack的功能,例如bundle文件的優化,資源管理和環境變量注入等。基本上,loader作不到的事情,plugin能夠用來處理。ios

  • mode,設置webpack的模式,值有development、production、none;git

// 即對應的設置process.env.NODE_ENV的值

// process.env.NODE_ENV的值爲'development'
// 開啓一些和本地開發相關的優化
mode: 'development'

// process.env.NODE_ENV的值爲'production'
// 開啓一些和生成相關的優化
mode: 'production'

// process.env.NODE_ENV的值爲'none'
// 不開啓任何優化
mode: 'none'
複製代碼

loader

  • 解析css
// 本地安裝
// css-loader 用於將導入的css文件解析成commonJs
// style-loader 用於將css以style標籤插入head中
cnpm i css-loader style-loader -D

module: {
    rules: [
        test: /.css$/,
        // 解析規則是從右到左的
        // 因此須要先解析成commonJs模塊
        // 而後在以style標籤形式插入到head中
        use: [
            'style-loader',
            'css-loader'
        ]
    ]
}
複製代碼
  • 解析less
// 本地安裝less和less-loader
cnpm i less less-loader -D

// 處理less文件
module: {
    rules: [
        test: /.less$/,
        // 先將less處理成css
        // 而後再按css的規則去處理
        use: [
            'style-loader',
            'css-loader',
            'less-loader'
        ]
    ]
}
複製代碼
  • 解析圖片
// 安裝file-loader,用於解析圖片
cnpm i file-loader -D

// 配置
module: {
    rules: [
        test: /.(jpg|jepg|png|gif)$/,
        use: 'file-loader'
    ]
}
複製代碼
  • 解析字體文件
// 用的也是file-loader
module: {
    rules: [
        test: /.(woff|woff2|eot|ttf|otf|svg)$/,
        use: 'file-loader'
    ]
}

// 例如阿里雲圖表的引入
// 在css/less中引入圖標
@font-face {
    font-family: 'iconfont';
    src: url('./font/iconfont.eot');
    src: url('./font/iconfont.eot?#iefix') format('embedded-opentype'),
        url('./font/iconfont.woff2') format('woff2'),
        url('./font/iconfont.woff') format('woff'),
        url('./font/iconfont.ttf') format('truetype'),
        url('./font/iconfont.svg#iconfont') format('svg');
}
.iconfont {
    font-family: "iconfont" !important;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

// 頁面內使用
<i class="iconfont">&#xe654;</i>
複製代碼

文件監聽

// 方式1,經過watch命令
// 增長npm script命令
"scripts": {
    "watch": "webpack --watch"
}

// 方式2,webpack.config.js中增長配置
module.exports = {
    watch: true,
    watchOptions: {
        // 指定忽略監控的文件,能提高一些性能
        ignored: /node_modules/,
        // 監聽到變化後,等待多長時間纔去執行
        aggregateTimeout: 3000,
        // 監聽變化是經過輪詢文件系統有麼有發生變化
        // 這裏設置每秒的輪詢次數
        poll: 1000
    }
}
複製代碼

這兩種方案,都有一個弊端,就是不會自動刷新,須要咱們在瀏覽器手動刷新才能看到變化。
開發中,咱們更經常使用的是使用熱更新。github

  • webapck-dev-server熱更新(簡稱WDS)
// 配置命令
// --open是服務啓動完成後,自動打開瀏覽器
"script": {
    "dev": "webpack-dev-server --open"
}

// 配置webpack.config.js
const webpakc = require('webpack');

moudle.exports = {
    // 熱更新的mode須要是開發模式
    mode: 'development',
    // 須要配置下面這個插件一塊兒使用
    // 在derServer開啓了hot: true以後,其實不用手動引入
    // webpack會自動引入這個插件
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: {
        contentBase: './dist',
        hot: true
    }
}
複製代碼

WDS不會刷新瀏覽器。
WDS不輸出文件,只是放在內存中,所以速度更快。
watch是輸出到磁盤中。

// 熱更新原理:
// 1.啓動階段:
(1)文件經過webpack-complier編譯後,將生成的文件傳遞給bundle-server,
    bundle-server開啓一個服務來支持文件經過相似localhsot:8080的方式在browser訪問
(2)HMR-Server在生成的文件中注入一個HMR-Runtime運行時,
    用來和brwoser創建鏈接通訊,以便在文件更新時通知browser
// 2.文件更新階段
(1)文件經過webpack-complier編譯後,將更新的內容傳遞給HMR-Server。
(2)HMR和HMR-Runtime通訊,將更新的內容一般以json的形式傳遞,
    HMR-Runtime局部更新bundle.js的文件內容。
複製代碼

image

補充:
(1)webpack-complier:將文件生成bundles.js
(2)HMR—Server:將熱更新的文件傳遞給HMR-Runtime
(3)HMR-Runtime:被注入到bundle.js,用來更新文件的變化
(4)bundles.js是構建後輸出的文件

  • 額外先說明區分dev和prod的配置
    (1)建立兩個配置文件

image

(2)package.json中區分dev和prod的命令

image
(3)兩個文件的配置要區分,一些配置只能用在dev,例如WDS;一些配置只能用於prod,例如MiniCssExtractPlugin;注意區分mode類型:

mode: 'production', // prod
mode: 'development', // dev
複製代碼

文件指紋

咱們常常看到的帶有各類相似hash(例如:app.38972982.js)的文件,這就能夠簡單理解爲文件指紋。

生成文件指紋的方式有三種:

  • Hash:根據構建目錄生成,只要項目中有文件修改,則整個項目構建出來的指紋都會改變。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[hash].js'
},
複製代碼

image

  • ChunkHash:和webpack打包到chunk有關,不一樣entry會生成不一樣的ChunkHush。chunk之間互不影響。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[chunkhash:8].js'
},
複製代碼

image

  • ContentHash:根據文件內容生成hash,只要文件內容不變,生成的ContentHash就不變。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[contenthash].js'
},
複製代碼

image

補充:
(1)冒號後面指定生成的指紋的位數。

(2)通常咱們打包的chunk文件,即js使用chunkhash模式;css使用contenthash模式;
(3)file-loader的name設置,指定打包後的指紋策略:

image

[path]能夠在生成的文件中生成對應的和源碼文件同樣 的目錄。

css打包成文件

  • 抽離css文件
// 安裝本地插件
cnpm i mini-css-extract-plugin -D

// 引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 使用
plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]_[contenthash:8].css'
    })
]

// 注意,該插件不能和style-loader使用,
// 由於style-loader是用來將css以style的方式插入的
{
    test: /.css$/,
    use: [
        // 須要將loader中css/less等文件裏面的style-loader,
        // 替換成該插件
        MiniCssExtractPlugin.loader,
        'css-loader'
    ]
},
複製代碼
  • 壓縮css文件
// 安裝壓縮css插件
cnpm i optimize-css-assets-webpack-plugin -D
// 同時須要配合cssnano插件使用
cnpm i cssnano -D

// 使用
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

plugins: [
    new OptimizeCssAssetsWebpackPlugin({
        assetsNameReg: /\.css$/g,
        cssProcessor: require('cssnano')
    })
]
複製代碼
  • 自動增長css兼容
// 首先安裝postcss-loader和autoprefixer
cnpm i postcss-loader autoprefixer -D

// 對樣式處理文件增長autoprefixer配置
// 處理css私有前綴和兼容
// 例如,增長以下配置
{
    test: /.less$/,
    use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        'less-loader',
        // 增長autoprefixer處理
        {
            loader: 'postcss-loader',
            options: {
                plugins: () => [
                    require('autoprefixer')({
                        // 經過browsers選項指定兼容的版本
                        // 推薦在webpack中配置在package.json中
                        browsers: [
                            'last 2 version',
                            '>1%',
                            'ios 7'
                        ]
                    })
                ]
            }
      }
    ]
}
複製代碼

autoprefixer配置文檔

  • px轉換成rem
// rem的適配,引用lib-flexible,生產使用
cnpm i lib-flexible -S

// px自動轉換成rem,本地安裝
cnpm i px2rem-loader -D

// 配置loader,在打包時自動將px轉換成rem
plugins: [
    // 處理less文件
    {
        test: /.less$/,
        use: [
            // 省略其餘loader
            ……
            // 將px轉換成rem
            {
                loader: 'px2rem-loader',
                options: {
                    // 對應比例,750px設計稿
                    remUnit: 75,
                    // 轉換成rem時保留的小數位
                    remPrecision: 8
                }
            }
        ]
      },
]
複製代碼

生成html模版

// 安裝html-webpack-plugin插件
cnpm i html-webpack-plugin -D

// 使用
plugins: [
    new HtmlWebpackPlugin({
        template: path.join(__dirname, 'src/index.html'),
        filename: 'index.html',
        chunks: ['react'],
        inject: true,
        minify: {
            // html5
            html5: false,
            // 最小化css
            minifyCSS: true,
            // 最小化js
            minifyJS: true,
            // 合併空格
            collapseWhitespace: true,
            // 移除註釋
            removeComments: true,
            // 是否保留換行
            preserveLineBreaks: false
        }
    })
]
複製代碼

(1)template: 指定生成的html模板
(2)filename: 指定生成的文件名稱
(3)chunks:指定將哪些chunk插入到html中
(4)inject:true/false,是否將chunk插入到html的body中;'body'表示插入到body中,'head'表示插入到head中; (5)minify:設置壓縮參數。
html-webpack-plugin官網

自動清理構建產物

  • 手動清理很麻煩
// 終端輸入
rm -rf dist
複製代碼
  • 利用webpack在每次打包時自動清理以前的產物
// 安裝clean-webpack-plugin
cnpm i clean-webpack-plugin -D

// 使用
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 之前版本引入時,不須要花括號
const CleanWebpackPlugin = require('clean-webpack-plugin');

// 使用
plugins: [
    new CleanWebpackPlugin()
]
複製代碼

文檔地址

靜態資源內聯

// 本地安裝raw-loader,這裏裝的是0.5.0的版本
cnpm i raw-loader@0.5.0 -D

// 在html文件中
// 例如將公共的meta資源內聯
<head>
    <!-- 只須要在對應的位置插入內聯代碼 -->
    ${ require('raw-loader!../meta.html') }
</head>

// meta.html示例
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">

// 內聯js,例如lib-flexible.js
// 咱們須要將它內聯在head中加載來保證更快
<head>
    <script>
        // 咱們這裏先對其進行babel轉譯後再內聯
        ${ require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') }
    </script>
</head>
複製代碼

內聯後生成的html效果以下:

image

更多實用黑科技未完待續,持續更新中~~~

百尺竿頭、日進一步。 我是愣錘,歡迎交流與分享。 若是以爲喜歡,記得點贊收藏哦~~

相關文章
相關標籤/搜索