【Webpack的使用指南 02】Webpack的常用解決方案


前言

說是解決方案實際上更像是webpack的插件索引。
寫這一篇的目的是為了形成一個索引,將來要用時直接來查找即可。

索引目錄

  1. 自動構建HTML,可壓縮空格,可給引用的js加版本號或隨機數:html-webpack-plugin
  2. 處理CSS:css-loader與style-loader
  3. 處理LESS:less-loade與less
  4. 提取css代碼到css文件中: extract-text-webpack-plugin
  5. 開發環境下的服務器搭建:webpack-dev-server
  6. 解析ES6代碼:babel-core babel-preset-env babel-loader
  7. 解析ES6新增的對象函數:babel-polyfill
  8. 解析react的jsx語法:babel-preset-react
  9. 轉換相對路徑到絕度路徑:nodejs的path模塊
  10. 給文件加上hash值:[chunkhash],[hash]
  11. 清空輸出文件夾之前的輸出文件:clean-webpack-plugin
  12. 模塊熱替換:NamedModulesPlugin和HotModuleReplacementPlugin
  13. 環境變量
  14. 跨平台使用環境變量: cross-env
  15. 處理圖片路徑: file-loader和html-loader
  16. 圖片壓縮:image-webpack-loader
  17. 定位源文件代碼:source-map
  18. 分離生產環境和開發環境的配置文件
  19. webpack輸出文件體積與交互關系的可視化:webpack-bundle-analyzer

1.自動構建HTML,可壓縮空格,可給引用的js加版本號或隨機數:html-webpack-plugin

解決方案:使用插件 html-webpack-plugin
webpack.config.js如下:

module.exports = {
  entry: './src/app.js',
  output: {
    path: __dirname + '/dist',
    filename: 'app.bundle.js'
  },
  plugins: [new HtmlWebpackPlugin({
    template: './src/模板文件.html',
    filename: '構建的.html',
    minify: {
      collapseWhitespace: true,
    },
    hash: true,
  })]
};

注意要有path,因為這個輸出的html需要知道輸出目錄

2.處理CSS:css-loader與style-loader

loader用於對模塊的源代碼進行預處理轉換。

解決方案:使用css-loaderstyle-loader

看一下項目結構:
引用了css的js

此時運行webpack命令會拋出錯誤:
webpack不能打包css

接下來安裝 css-loader 和 style-loader

npm install --save-dev css-loader style-loader

再修改webpack.config.js為:
紅框中為新加的配置

這其中rules數組就是loader用來的匹配和轉換資源的規則數組。
test代表匹配需轉換文件的正則表達式,而圖中表示匹配所有以css結尾的文件。
而use數組代表用哪些loader去處理這些匹配到的文件。

此時再運行webpack,打包后的文件bundle.js就包含了css代碼。
其中css-loader負責加載css,打包css到js中。
而style-loader負責生成:在js運行時,將css代碼通過style標簽注入到dom中。

3.處理LESS:less-loade與less

解決方案:使用less-loader
但是用less-loader只是將LESS代碼轉換為css代碼。如果要打包文件到js中,還是需要用到上面提到的css-loader和style-loader。

看一下項目結構:
less項目結構

然后app.js的代碼為:

import styles from './app.less';

console.info('我是一個js文件123')

為了解決這種情況,首先要安裝 less-loader,而less-loader是基於less的,所以也要安裝less。

npm i --save-dev less less-loader

修改webpack.config.js為:

module: {
  rules: [
    {
      test: /\.less$/,
      use: [ 'style-loader', 'css-loader', 'less-loader' ]
    }
  ]
}

4.提取css代碼到css文件中: extract-text-webpack-plugin

很多時候我們想要的效果並不是想要把幾個LESS或者CSS處理好后,打包到一個js中,而是想要把它打包到一個css文件中。
此時就有了插件 extract-text-webpack-plugin
首先進行安裝

npm i --save-dev extract-text-webpack-plugin

然后修改webpack.config.js為:
紅框中為新加或修改的配置

與原配置對比可以發現,比html-webpack-plugin這個插件多做了一步,就是在匹配和轉換規則里面的use中使用了ExtractTextPlugin.extract。
注意這里的fallback表示,在提取文件失敗后,將繼續使用style-loader去打包到js中。
此時運行webpack
可以發現輸出目錄build下生成了一個style.css文件,也就是我們在webpack.config.js中期望生成的文件,並且在生成的demo.html中被引用了。

5.開發環境下的服務器搭建:webpack-dev-server

webpack-dev-server可以在本地搭建一個簡單的開發環境用的服務器,自動打開瀏覽器,而且還可以達到webpack -watch的效果。
首先安裝一下:

npm i -g  webpack-dev-server
npm i --save-dev webpack-dev-server

這里不需要改動webpack.config.js,直接運行命令

webpack-dev-server

查看控制台輸出:
控制台輸出

顯示項目運行在http://localhost:8080/
webpack的輸出目錄的路徑在/下面
並且這個服務器會自動識別輸出目錄下名為index的HTML文件,而我們之前輸出的文件名為demo.html。
所以還需要將之前html-webpack-plugin中配置的filename改為index.html,或者直接用http://localhost:8080/demo.html也行。
當我們修改了源代碼后,打開的網頁還會自動更新。

為了更靈活的應用開發環境的服務器,也可以在webpack.config.js中加入如下代碼:
image.png

devServer配置 功能
port 修改端口為8787,而不是默認的8080。
open 為true表示會自動打開瀏覽器,而不是需要我們再手動打開瀏覽器並在里面輸入http://localhost:8080。
compress 對本地server返回的文件提供gzip壓縮
index 指定網站首頁映射的文件,默認為index.html

6.解析ES6代碼:babel-core babel-preset-env babel-loader

這里說是ES6,實際上可以認為是ECMAScript的高版本代碼,只是代指而已。
babel的作用是將瀏覽器還未支持的這些高版本js代碼轉換成可以被指定瀏覽器支持的js代碼。

這里列出可以轉換的大致語法:
babel-preset-env支持的轉換

那么首先就需要安裝babel

npm install babel-core babel-preset-env --save-dev

然后,為了和webpack結合起來,要用到babel-loader

npm install babel-loader --save-dev

然后在webpack.config.js的rules數組中增加以下代碼:

{
  test: /\.js$/,
  exclude: /(node_modules)/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['env']
    }
  }
}

這行代碼的意思是用babel-loader解析除了node_modules文件下的所有js文件。
而babel-loader就是用babel去解析js代碼。
options的內容類似於.babelrc文件的配置,有了這個就不需要.babelrc文件了。
presets表示預處理器,現在的babel不像以前需要很多預處理器了,只需要env這一個就夠了。

修改之前的app.js中的代碼為:

console.info('我是一個js文件123')
const doSomething=() => {
  console.info('do do do')
}

使用webpack命令后,可以看到我們最后的打包js文件中代碼變成了這樣:
image.png

7.解析ES6新增的對象函數:babel-polyfill

以下為這些新增函數:
babel-polyfill支持的轉換

安裝:

npm install --save-dev babel-polyfill

為了確保babel-polyfill被最先加載和解析,所以一般都是講babel-polyfill在最開始的腳本中引入。
而在webpack中,就是在放到entry中,所以需要修改webpack.config.js中的配置為:

紅框中為修改的部分

8.解析react的jsx語法:babel-preset-react

安裝

npm install --save-dev babel-preset-react

配置:
修改后的配置

這里是匹配所有以js或者jsx結尾的文件,並用 babel-preset-env和babel-preset-react進行解析

9.轉換相對路徑到絕度路徑:nodejs的path模塊

這里首先介紹一下nodejs的path模塊的一個功能:resolve。
將相對路徑轉換為絕對路徑。
在最開始引用path模塊

var path = require('path');

然后可以在輸出設置那里修改代碼為:

  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },

和我們原來的代碼沒有任何區別。

10.給文件加上hash值:[chunkhash],[hash]

hash和chunkhash有區別,hash的話輸出的文件用的都是同一個hash值,而chunkhash的話是根據模塊來計算的,每個輸出文件的hash值都不一樣。
直接將輸出文件改為

output: {
  path: path.resolve(__dirname, 'build'),
  filename: 'bundle.[chunkhash].js'
},

[chunkhash]就代表一串隨機的hash值

11.清空輸出文件夾之前的輸出文件:clean-webpack-plugin

當我們像上面一樣不斷改變輸出文件時,之前的輸出文件並沒有去掉。
為了解決這個問題就需要clean-webpack-plugin。
首先安裝

npm i clean-webpack-plugin --save-dev

然后引用插件,並聲明每次生成輸出需要清空的文件夾

var CleanWebpackPlugin = require('clean-webpack-plugin');
var pathsToClean = [
  'build',
]

再在插件配置中加入:

new CleanWebpackPlugin(pathsToClean)

12.模塊熱替換:NamedModulesPlugin和HotModuleReplacementPlugin

之前的webpack-dev-server提供了監聽功能,只要代碼改變,瀏覽器就會刷新。
但是模塊熱替換是不會刷新瀏覽器,只刷新修改到的那部分模塊。
模塊熱替換無需安裝。
首先需要引入模塊

var webpack = require('webpack')

其實插件中加入:

new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()

此時運行webpack可能會報錯,我們需要把之前在輸出環境中寫的[chunkhash]改為[hash]

13.環境變量

可以在腳本中這么寫:

"scripts": {
"dev": "webpack-dev-server",
"prod": "set NODE_ENV=production && webpack -p"
},

這樣在webpack.config.js中這樣修改上面的東西:

if (isProduction) {
    config.output.filename = 'bundle.[chunkhash].js'
} else {
    config.plugins.push(new webpack.NamedModulesPlugin())
    config.plugins.push(new webpack.HotModuleReplacementPlugin())
}

這樣就可以根據環境的不同來運行不同的配置

14.跨平台使用環境變量: cross-env

上述設置環境變量的腳本中只有在window下才有效,在linux和mac上需要使用

"prod": "NODE_ENV=production webpack -p"

為了解決這個問題,使得不同平台的人能公用一套代碼,我們可以使用cross-env。
首先進行安裝:

npm i --save-dev cross-env

然后命令直接使用類似於mac上的用法即可

"prod": "cross-env NODE_ENV=production webpack -p"

15.處理圖片路徑: file-loader和html-loader

file-loader可以用來處理圖片和字體文件在css文件中的路徑問題,輸出的css文件中會引用輸出的文件地址。
html-loader可以用來處理html中,比如img元素的圖片路徑問題。
首先安裝

npm i --save-dev file-loader html-loader

配置:

        {
            test: /\.(gif|png|jpe?g|svg)$/i,
            use: {
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: 'src/images/'
                }
            }
        },
        {
            test: /\.html$/,
            use: [{
                loader: 'html-loader',
                options: {
                    minimize: true
                }
            }],
        }

16.圖片壓縮:image-webpack-loader

安裝:

npm i --save-dev image-webpack-loader

配置:

    {
            test: /\.(gif|png|jpe?g|svg)$/i,
            use: [{
                    loader: 'file-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'images/'
                    }
                },
                {
                    loader: 'image-webpack-loader',
                    options: {
                        bypassOnDebug: true,
                    }
                }
            ]
        },

這里的options中也可以具體配置各個圖片類型的壓縮質量

17.定位源文件代碼:source-map

如果我們用web-dev-server運行我們的輸出文件,發現其中有些BUG,然后打開開發者工具取定位文件的時候,只會定位到我們的輸出文件。
而這些輸出文件是經過處理的,我們只有找到我們的源文件代碼,然后進行相應的修改才能解決問題。
於是這里我們需要用到source-map。
很簡單,在webpack.config.js中加入如下配置即可:

devtool: 'source-map',

就這么簡單,還不需要安裝什么插件。
但是這只對js有效,如果我們的css出現錯誤了呢,答案就是如下配置:
在這些loader后面加上?sourceMap即可

18.分離生產環境和開發環境的配置文件

之前我們通過在命令中設置環境變量,並且通過環境變量來判斷環境來進行不同的配置。
現在我們用官方推薦的方法來分離生產環境和開發環境的配置文件。
我們將webpack.config.js分為三個文件

  • webpack.common.js
  • webpack.dev.js
  • webpack.prod.js

其中webpack.common.config.js為生產環境和開發環境共有的配置,dev為開發環境獨有的配置,prod為生成環境獨有的配置。
而想要合成真正的配置文件,還需要一個工具:webpack-merge。

  npm install --save-dev webpack-merge

以下是我們之前的webpack.config.js代碼:

var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var path = require('path')
var webpack = require('webpack')

var pathsToClean = [
    'build',
]

var isProduction = process.env.NODE_ENV === 'production'

var config = {
    entry: ['babel-polyfill', './src/app.js'],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name].[hash].js'
    },
    devtool: 'source-map',
    devServer: {
        port: 8787,
        open: true,
        compress: true,
        index: 'demo.html'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './template/index.html',
            filename: 'demo.html',
            minify: {
                collapseWhitespace: true,
            },
            hash: true
        }),
        new ExtractTextPlugin({ filename: 'style.css', allChunks: false }),
        new CleanWebpackPlugin(pathsToClean)
    ],
    module: {
        rules: [{
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader?sourceMap']
                })
            },
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader?sourceMap', 'less-loader?sourceMap']
                })
            },
            {
                test: /\.jsx?$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['env', 'react']
                    }
                }
            },
            {
                test: /\.(gif|png|jpe?g|svg)$/i,
                use: [{
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]',
                            outputPath: 'images/'
                        }
                    },
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            bypassOnDebug: true,
                        }
                    }
                ]
            },
            {
                test: /\.html$/,
                use: [{
                    loader: 'html-loader',
                    options: {
                        minimize: true
                    }
                }],
            }
        ]
    }
};

if (isProduction) {
    config.output.filename = '[name].[chunkhash].js'
} else {
    config.plugins.push(new webpack.NamedModulesPlugin())
    config.plugins.push(new webpack.HotModuleReplacementPlugin())
}

module.exports = config

接下來分為三個文件,webpack.common.js:
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var path = require('path')
var webpack = require('webpack')

var pathsToClean = [
    'build',
]

var isProduction = process.env.NODE_ENV === 'production'

module.exports = {
    entry: ['babel-polyfill', './src/app.js'],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name].[chunkhash].js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './template/index.html',
            filename: 'demo.html',
            minify: {
                collapseWhitespace: true,
            },
            hash: isProduction
        }),
        new ExtractTextPlugin({ filename: '[name].[contenthash].css', allChunks: false }),
        new CleanWebpackPlugin(pathsToClean)
    ],
    module: {
        rules: [{
                test: /\.jsx?$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['env', 'react']
                    }
                }
            },
            {
                test: /\.(gif|png|jpe?g|svg)$/i,
                use: [{
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]',
                            outputPath: 'images/'
                        }
                    },
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            bypassOnDebug: true,
                        }
                    }
                ]
            },
            {
                test: /\.html$/,
                use: [{
                    loader: 'html-loader',
                    options: {
                        minimize: true
                    }
                }],
            }
        ]
    }
};

然后是webpack.dev.js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = merge(common, {
    output: {
        filename: '[name].[hash].js'
    },
    devtool: 'source-map',
    devServer: {
        port: 8787,
        open: true,
        compress: true,
        index: 'demo.html'
    },
    plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin()
    ],
    module: {
        rules: [{
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader?sourceMap']
                })
            },
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader?sourceMap', 'less-loader?sourceMap']
                })
            }
        ]
    }
});

最后是webpack.prod.js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = merge(common, {
    module: {
        rules: [{
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader']
                })
            },
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'less-loader']
                })
            }
        ]
    }
});

然后修改一下package.json中的腳本即可

  "scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
    "prod": "cross-env NODE_ENV=production webpack -p --config webpack.prod.js"
},

19. webpack輸出文件體積與交互關系的可視化:webpack-bundle-analyzer

安裝:

npm install --save-dev webpack-bundle-analyzer

然后修改webpack.config.js:

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = merge(common, {
  // ...
  plugins: [
    new BundleAnalyzerPlugin({ analyzerPort: 8919 })
  ],
});

這里的analyzerPort為打包后的本地展示網頁的端口,默認是8888。

然后webpack即可。

效果圖如下:

總結

各個插件以及loader的玩法還有很多,這里不具體介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM