webpack4.x最詳細使用講解三

七、項目優化及拓展

7.1 代碼分離

在當前的開發環境都是提倡模塊化,webpack自然不例外,我們前面的webpack.config.js配置文件,其實也沒配置多少東西就這麼多了,要是以後增加了更多配置,豈不是看得眼花繚亂,所以最好的方法就是把它拆分,方便管理:

1. 我們在根目錄下新建三個文件,分別爲webpack.common.jswebpack.dev.jswebpack.prod.js,分別代表公共配置文件、開發環境配置文件、生產環境(指項目上線時的環境)配置文件。

2. 安裝一個合併模塊插件:

cnpm i webpack-merge -D

3. 將webpack.config.js的代碼拆分到上述新建的三個文件中,然後把webpack.config.js文件刪除,具體如下:

// webpack.common.js
const path = require('path');  // 路徑處理模塊
const webpack = require('webpack');  // 這個插件不需要安裝,是基於webpack的,需要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包後的文件存放的地方
        filename: "bundle.js" //打包後輸出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序,因爲調用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是這個順序,因爲調用loader是從右往左編譯的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use選擇如果有多項配置,可寫成這種對象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模塊
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權所有,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,並傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
}
// webpack.dev.js
const merge = require('webpack-merge');  // 引入webpack-merge功能模塊
const common = require('./webpack.common.js'); // 引入webpack.common.js

module.exports = merge(common, {   // 將webpack.common.js合併到當前文件
    devServer: {
        contentBase: "./dist",   // 本地服務器所加載文件的目錄
        port: "8088",  // 設置端口號爲8088
        inline: true,  // 文件修改後實時刷新
        historyApiFallback: true, //不跳轉
        hot: true     //熱加載
    }
})
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = merge(common, { // 將webpack.common.js合併到當前文件
    devtool: 'source-map',  // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
    ]
})

此時我們的項目目錄如下:

4. 設置package.json的scripts命令:

{
  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "clean-webpack-plugin": "^0.1.19",
    "css-loader": "^1.0.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.9.4",
    "react": "^16.6.0",
    "react-dom": "^16.6.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10",
    "webpack-merge": "^4.1.4"
  }
}

 我們把build命令改爲了webpack --config webpack.prod.js,意思是把打包配置指向webpack.prod.js配置文件,而之前我們只需要使用一個webpack命令爲什麼就可以運行了?因爲webpack命令是默認指向webpack.config.js這個文件名稱了,現在我們把文件名稱改了,所以就需要自定義指向新的文件,dev命令中的指令也同理。

然後我們運行npm run buildnpm run dev,效果應該和我們分離代碼前是一樣的。

注:說道package.json文件,順便就多提幾句,因爲也許有些朋友可能對我們安裝模塊時加的-D-S-g命令存在一些疑惑,因爲不知道什麼時候加什麼尾綴。
其實這個package.json文件是用於我們安裝依賴的,可以把它當成一份依賴安裝說明表,就是如果我們把項目上傳或者發給其他的開發同事,肯定不會把/node_modules文件夾也發送過去,因爲這太大了,不現實也沒必要。
開發同事只需要有這份package.json文件,然後npm install就可以把我們所需要的依賴都安裝下來,但前提是package.json文件上有記錄,這就是安裝模塊時加上-D,-S命令的原因。
-D的全稱是--save-dev指開發環境時需要用到的依賴,會記錄在package.json文件中的devDependencies選項中,而-S--save是指生產環境也就是上線環境中需要用到的依賴,會記錄在package.json文件中的dependencies選項中,-g的全稱是--global指安裝全局命令,就是我們在本電腦的任何項目中都能使用到的命令,比如安裝cnpm這個淘寶鏡像命令就會用到-g命令。
所以我們在安裝模塊時一定不要忘了加上對應的尾綴命令,讓我們的模塊有跡可循,否則其他的開發同事接手你的項目的話,會不會下班後(放學後)在門口等你就不知道了。

扯遠了,希望不要嫌棄,也是想講得更詳細嘛!

7.2 多入口多出口

到目前爲止我們都是一個入口文件和一個出口文件,要是我不止一個入口文件呢?下面我們來試試:

webpack.common.js中的entry入口有三種寫法,分別爲字符串、數組和對象,平時我們用得比較多的是對象,所以我們把它改爲對象的寫法,首先我們在src文件夾下新建two.js文件,名稱任意。因爲有多個入口,所以肯定得多個出口來進行一一對應了,所以entryoutput配置如下:

// webpack.common.js
...
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"),
        two: path.join(__dirname, "/src/two.js")
    }, 
    output: {
        path: path.join( __dirname, "/dist"), //打包後的文件存放的地方
        filename: "[name].js" //打包後輸出文件的文件名
    },
    ...
}

 

// two.js
function two() {
    let element = document.createElement('div');
    element.innerHTML = '我是第二個入口文件';
    return element;
}

document.getElementById('root').appendChild(two());

然後我們運行npm run build打包後發現/dist文件夾下會多出two.js文件,同時index.html也會自動將two.js引入,然後我們運行npm run dev顯示如下:

 

7.3 增加css前綴、分離css、消除冗餘css、分離圖片

1.增加css前綴

平時我們寫css時,一些屬性需要手動加上前綴,比如-webkit-border-radius: 10px;,在webpack中我們能不能讓它自動加上呢?那是必須的,首先肯定得安裝模塊了:

cnpm i postcss-loader autoprefixer -D

安裝好這兩個模塊後,在項目根目錄下新建postcss.config.js文件:

// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')  // 引用autoprefixer模塊
    ]
}

在style.css中增加以下樣式:

/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產生前綴 */
}

修改webpack.common.js文件中的css-loader配置:

...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: [            
                    {loader: 'style-loader'}, // 這裏採用的是對象配置loader的寫法
                    {loader: 'css-loader'},
                    {loader: 'postcss-loader'} // 使用postcss-loader
                ]  
            },
            ...
        ]
    },
    ...
}

然後我們運行npm run dev後css樣式中會自動添加前綴,顯示如下:

2.分離css

雖然webpack的理念是把css、js全都打包到一個文件裏,但要是我們想把css分離出來該怎麼做呢?

cnpm i extract-text-webpack-plugin@next -D  // 加上@next是爲了安裝最新的,否則會出錯

 安裝完以上插件後在webpack.common.js文件中引入並使用該插件:

// webpack.common.js
...
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分離插件

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({  // 這裏我們需要調用分離插件內的extract方法
                    fallback: 'style-loader',  // 相當於回滾,經postcss-loader和css-loader處理過的css最終再經過style-loader處理
                    use: ['css-loader', 'postcss-loader']
                })
            },
            ...
        ]
    },
    plugins: [
        ...
        new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css
    ]
}

運行npm run build後會發現/dist文件夾內多出了/css文件夾及index.css文件。

3.消除冗餘css

有時候我們css寫得多了,可能會不自覺的寫重複了一些樣式,這就造成了多餘的代碼,上線前又忘了檢查,對於這方面,我們應該儘量去優化它,webpack就有這個功能。

cnpm i purifycss-webpack purify-css glob -D

安裝完上述三個模塊後,因爲正常來說是在生產環境中優化代碼,所以我們應該是在webpack.prod.js文件中進行配置,引入clean-webpack-pluginglob插件並使用它們:

// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob');  // 引入glob模塊,用於掃描全部html文件中所引用的css

module.exports = merge(common, {   // 將webpack.common.js合併到當前文件
    devtool: 'source-map',  // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步掃描所有html文件中所引用的css
        })
    ]
})

我們在style.css文件中增加一些多餘的代碼試試:

/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產生前綴 */
}

.a{                 /* 冗餘css */
    color: black;     
}

.b{                 /* 冗餘css */
    width: 50px;
    height: 50px;
    background: yellow;
}

然後我們運行npm run build後發現打包後的index.css中是沒有多餘的.a和.b代碼的:

/* index.css */

body {
  background: #999;
}

#root div {
  width: 200px;
  margin-top: 50px;
  -webkit-transform: rotate(45deg);
  transform: rotate(45deg);
  /* 這個屬性會產生前綴 */
}
/*# sourceMappingURL=index.css.map*/

4.處理圖片

到目前爲止我們還沒講到圖片的問題,如果要使用圖片,我們得安裝兩個loader:

// 雖然我們只需使用url-loader,但url-loader是依賴於file-loader的,所以也要安裝
cnpm i url-loader file-loader -D 

然後在webpack.common.js中配置url-loader:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
                use: [
                    {
                        loader: 'url-loader'  // 使用url-loader
                    }
                ]
            },
            ...
        ]
    },
    ...
}

我們修改一下style.css,把背景改爲圖片背景:

/* style.css */
body {
    background: url(../images/coffee.png) top right repeat-y;  /* 設爲圖片背景 */
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產生前綴 */
}

.a{
    color: black;
}

.b{
    width: 50px;
    height: 50px;
    background: yellow;
}

運行npm run dev後顯示如下:

但是背景圖片變成了base64,因爲webpack會自動優化圖片,減少發送請求,但是如果我想把它變成路徑的該怎麼做?

我們可以把webpack.common.js的loader配置更改一下,增加options選項:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000  // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,所以不會被轉化
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

然後我們運行npm run build後,再運行npm run dev,額,圖片是沒有轉成base64了,但是圖片怎麼不顯示了?

問題就出在路徑上,我們之前圖片的路徑是在../images文件夾下,但是打包出來後沒有這個路徑了,圖片直接和文件同級了,所以我們需要在webpack.common.js中給它設置一個文件夾:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,所以不會被轉化
                            outputPath: 'images'  // 設置打包後圖片存放的文件夾名稱
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

 繼續npm run build打包再npm run dev運行,我的天!圖片還是不顯示!

調試工具上看圖片路徑有images文件夾了,但是我的../呢?

這又涉及到配置路徑的問題上了,我們還需要在css-loader中給背景圖片設置一個公共路徑publicPath: '../',如下:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader'],
                    publicPath: '../'  // 給背景圖片設置一個公共路徑
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,所以不會被轉化
                            outputPath: 'images'  // 設置打包後圖片存放的文件夾名稱
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

 現在再npm run build打包再npm run dev啓動,OK!沒毛病!

是不是很熱鬧?到現在我們不知不覺中也同時解決了圖片分離的問題,偷偷高興一下吧!

7.4 壓縮代碼

在webpack4.x版本中當你打包時會自動把js壓縮了,而且npm run dev運行服務器時,當你修改代碼時,熱更新很慢,這是因爲你修改後webpack又自動爲你打包,這就導致了在開發環境中效率很慢,所以我們需要把開發環境和生產環境區分開來,這時就體現出我們代碼分離的便捷性了,webpack.dev.js代表開發環境的配置,webpack.prod.js代表生產環境的配置,這時我們只要在package.json文件中配置對應環境的命令即可:

{
  ...
  "scripts": {
    "build": "webpack --config webpack.prod.js --mode production",
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
  },
  ...
  }
}

 --mode production表示打包時是生產環境,會自己將js進行壓縮,而--mode development表示當前是開發環境,不需要進行壓縮。這同時也解決了之前一直遺留的警告問題:

總結

好了,到現在我們基本把webapck常用的功能都走了一遍,寫得有點長,感謝你能仔細的看到這裏,希望能對你有所幫助,如果有發現不對的地方,也請多多指教。其實webpack還有很多功能,這裏也沒講述完全,但相信你現在對webpack也有了一定的瞭解,更多的webpack探索一定難不倒你!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章