從零開始webpack搭建項目

構建步驟

一、初始化

在終端執行npm init命令生成package.jsoncss

該命令會詢問項目名稱、描述、做者、入口、測試命令等等信息(能夠無論, 直接一路回車)html

1.1 初始化eslint

本地安裝 eslintnode

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

eslint官方提供了3種預安裝包, 使用eslint-config-standardreact

npm install eslint-config-standard eslint-plugin-standard eslint-plugin-promise -D
複製代碼

初始化eslint文件webpack

eslint --init
複製代碼

執行eslint --init命令後, 會提示一系列問題如圖,而後在根目錄下生成一文件.eslintrc.js, 修改extends爲standardios

// .eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "commonjs": true
    },
    "extends": "standard"
};
複製代碼

二、安裝webpackwebpack-cli

由於webpack4.x版本以後 webpack模塊一部分功能分到webpack.cli模塊, 因此二者都須要安裝,具體命令以下web

2.1 全局安裝webpack與webpack-cli模塊

npm install webpack webpack-cli --global
複製代碼

2.2 安裝本地webpack與webpack-cli模塊

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

提示

上述命令可採用簡寫,install可簡寫爲i,--global可簡寫爲-g,--save-dev可簡寫爲-D(這個命令是用於把配置添加到package.json的開發環境配置列表中,後面會提到),--save可簡寫爲-S正則表達式

npm i webpack -g               //這是安裝全局webpack命令
npm i webpack webpack-cli -D   //這是安裝本地項目模塊
複製代碼

2.3 在項目根目錄新建src和dist文件夾與index.html,src下創建hello.js與index.js

hello.js中 導出一個模塊npm

// hello.js
module.exports = function() {
    let hello = document.createElement('div');
    hello.innerHTML = "Hello World!";
    return hello;
};
複製代碼

index.js中引入該模塊json

const hello = require('./hello')
document.getElementById('root').appendChild(hello)
複製代碼

咱們打包時就只需把index.js模塊打包成bundle.js,而後供index.html引用便可,這就是最簡單的webpack打包原理

2.4 開始進行webpack打包

webpack全局安裝的狀況下npm install webpack webpack-cli -g

在終端執行如下打包命令

webpack src/index.js --output dist/bundle.js
複製代碼

output能夠簡寫o

2.5 經過配置文件來使用webpack

新建webpack.config.js, 簡單的配置入口, 出口配置

注:webpack.config.js文件中的__dirname是node.js中的一個全局變量,它指向當前執行腳本所在的目錄 即D:\GitLab\webpack4

注:path.join的功能是拼接路徑片斷。

有了這個配置文件,咱們只需在終端中運行webpack命令就可進行打包,這條命令會自動引用webpack.config.js文件中的配置選項,示例以下:

webpack

2.6 在package.json文件中配置打包命令

注:package.json中的script會按你設置的命令名稱來執行對應的命令。
{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "webpack4嚐鮮",
  "main": "index.js",
  "scripts": {
    "start": "webpack",
    "test": "npm run test"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2"
  }
}
複製代碼

咱們就能夠在終端中直接執行npm start命令來進行打包,start命令比較特殊,能夠直接npm加上start就能夠執行,若是咱們想起其餘的名稱,如build時,就須要使用npm run加上build,即npm run build命令。 如今咱們執行npm start命令

3 構建本地服務器

3.1 webpack-dev-server安裝本地服務器

Webpack提供了一個可選的本地開發服務器,這個本地服務器基於node.js構建,它是一個單獨的組件,在webpack中進行配置以前須要單獨安裝它做爲項目依賴

安裝命令:npm i webpack-dev-server -D

注意:devServer做爲webpack配置選項中的一項,如下是它的一些配置選項:

  • contentBase :設置服務器所讀取文件的目錄,當前咱們設置爲"./dist"
  • port :設置端口號,若是省略,默認爲8080
  • inline :設置爲true,當源文件改變時會自動刷新頁面
  • historyApiFallback :設置爲true,全部的跳轉將指向index.html
// webpack.config.js
const path = require('path')
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  output: {
    path: path.join(__dirname, '/dist'), // 打包後的文件存放的地方
    filename: 'bundle.js' // 打包後輸出文件的文件名
  },
  // 本地服務器配置
  devServer: {
    contentBase: './dist', // 本地服務器所加載文件的入口
    port: '8080', // 端口號8080
    inline: true, // 修改源碼文件後實時刷新
    historyApiFallback: true // 不跳轉
  }
}
複製代碼

在package.json 文件配置啓動服務器命令webpack-dev-server --open

// package.json
{
 ...
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open",
    "test": "npm run test"
  },
 ...
}
複製代碼

咱們把start命令名稱改成了build,這樣比較語義化,平時的腳手架也多數採用這個名稱,咱們用dev(development的縮寫,意指開發環境)來啓動本地服務器,webpack-dev-server就是啓動服務器的命令,--open是用於啓動完服務器後自動打開瀏覽器,這時候咱們自定義命令方式的便捷性就體現出來了,能夠多個命令集成在一塊兒運行,即咱們定義了一個dev命令名稱就能夠同時運行了webpack-dev-server和--open兩個命令

3.2 Source Maps調試配置

做爲開發,代碼調試固然少不了,那麼問題來了,通過打包後的文件,你是不容易找到出錯的地方的,Source Map就是用來解決這個問題的 經過以下配置,咱們會在打包時生成對應於打包文件的.map文件,使得編譯後的代碼可讀性更高,更易於調試。

// webpack.config.js
const path = require('path')
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  output: {
    path: path.join(__dirname, '/dist'), // 打包後的文件存放的地方
    filename: 'bundle.js' // 打包後輸出文件的文件名
  },
  // 本地服務器配置
  devServer: {
    contentBase: './', // 本地服務器所加載文件的入口
    port: '8080', // 設置端口號,若是省略,默認爲8080
    inline: true, // 設置爲true,當源文件改變時會自動刷新頁面
    historyApiFallback: false // 設置爲true,全部的跳轉將指向index.html
  },
  devtool: 'source-map' // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
}
複製代碼

配置好後,咱們再次運行npm run build進行打包,這時咱們會發如今dist文件夾中多出了一個bundle.js.map文件 若是咱們的代碼有bug,在瀏覽器的調試工具中會提示錯誤出現的位置,這就是devtool: 'source-map'配置項的做用。

四、Loaders

loaders是webpack最強大的功能之一,經過不一樣的loader,webpack有能力調用外部的腳本或工具,實現對不一樣格式的文件的處理,例如把scss轉爲css,將ES6六、ES7等語法轉化爲當前瀏覽器能識別的語法,將JSX轉化爲js等多項功能。

Loaders須要單獨安裝而且須要在webpack.config.js中的modules配置項下進行配置,Loaders的配置包括如下幾方面:

  • test:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)
  • loader:loader的名稱(必須)
  • include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選);
  • options:爲loaders提供額外的設置選項(可選)

4.1 配置css-loader

若是咱們要加載一個css文件,須要安裝配置style-loader和css-loader:

安裝css-loader與其依賴:npm i style-loader css-loader -D 配置文件如今爲

// webpack.config.js
const path = require('path')
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  output: {
    path: path.join(__dirname, '/dist'), // 打包後的文件存放的地方
    filename: 'bundle.js' // 打包後輸出文件的文件名
  },
  // 本地服務器配置
  devServer: {
    contentBase: './', // 本地服務器所加載文件的入口
    port: '8080', // 設置端口號,若是省略,默認爲8080
    inline: true, // 設置爲true,當源文件改變時會自動刷新頁面
    historyApiFallback: false // 設置爲true,全部的跳轉將指向index.html
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/, // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  }
}
複製代碼

在src文件夾下新建css文件夾,在css文件夾下新建index.css文件 而且在index.js中引入

import './css/index.css'
const hello = require('./hello')
document.getElementById('root').appendChild(hello)
複製代碼

4.2 配置SASS

安裝:

npm i sass-loader node-sass -D  // 由於sass-loader依賴於node-sass,因此還要安裝node-sass
複製代碼

配置SASS的rules

// webpack.config.js
const path = require('path')
module.exports = {
  ...
  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是從右往左編譯的
      }
    ]
  }
}
複製代碼

css文件夾下新建red.scss文件

$--red-color: red;
body{
  color: $--red-color;
}
複製代碼

index.js中引入red.scss

import './css/red.scss'
import './css/index.css'
const hello = require('./hello')
document.getElementById('root').appendChild(hello)
複製代碼

5 Babel

Babel 是一個 JavaScript 編譯器 Babel 是一個工具鏈,主要用於在舊的瀏覽器或環境中將 ECMAScript 2015+ 代碼轉換爲向後兼容版本(ES六、ES七、ES8...)的 JavaScript 代碼。如下是Babel 能夠爲你作的主要事情

  • (1)、轉換語法
  • (2)、Polyfill 實現目標環境中缺乏的功能 (經過 @babel/polyfill)
  • (3)、源代碼轉換 (codemods)
  • (4)、基於JavaScript進行拓展的語言, 例如JSX

5.1 Babel的安裝與配置

Babel實際上是幾個模塊化的包,其核心功能位於稱爲babel-core的npm包中,webpack能夠把其不一樣的包整合在一塊兒使用,對於每個你須要的功能或拓展,你都須要安裝單獨的包(用得最多的是解析ES6的babel-preset-env包和解析JSX的babel-preset-react包)。

npm install -D babel-loader @babel/core @babel/preset-env // babel-preset-env的env表示是對當前環境的預處理,而不是像之前使用babel-preset-es2015只能針對某個環境
複製代碼
const path = require('path')
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(js|jsx)$/,
        use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
          loader: 'babel-loader'
          // options: { // 後續Babel配置會單獨提取到.babelrc文件中
          //   presets: [ '@babel/preset-env' ] // 支持最新JS語法(ES六、ES七、ES8。。。)
          // }
        },
        exclude: /node_modules/ // 排除匹配node_modules模塊
      }
    ]
  }
}
複製代碼

6 插件(Plugins)

插件(Plugins)是用來拓展Webpack功能的,它們會在整個構建過程當中生效,執行相關的任務。

Loaders和Plugins經常被弄混,可是他們實際上是徹底不一樣的東西,能夠這麼來講,loaders是在打包構建過程當中用來處理源文件的(JSX,Scss,Less..),一次處理一個,插件並不直接操做單個文件,它直接對整個構建過程其做用。

6.1 插件如何使用

使用某個插件,須要經過npm進行安裝,而後在webpack.config.js配置文件的plugins(是一個數組)配置項中添加該插件的實例,下面咱們先來使用一個簡單的版權聲明插件.

// webpack.config.js
...
module.exports = {
  ...
  plugins: [
    new webpack.BannerPlugins('版權全部,翻版必究') // new一個插件的實例
  ]
}
複製代碼

新建.babelrc文件, 將Babel配置收取至.babelrc文件中

{
  "presets": [ "@babel/preset-env" ]
}
複製代碼

6.2 自動生成html文件(html-webpack-plugin)

咱們都是使用的模板index.html, 那麼怎麼自動引入打包生成以後的JS文件?HtmlWebpackPlugin插件就是用來解決這個問題的

安裝該插件npm i html-webpack-plugin -D

在src文件夾下新建index.html的文件模板(固然這個是可選的,由於就算不設置模板,HtmlWebpackPlugin插件也會生成默認html文件,這裏咱們設置模塊會讓咱們的開發更加靈活),以下:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
複製代碼

webpack.config.js中咱們引入了HtmlWebpackPlugin插件,並配置了引用了咱們設置的模板,以下:

// webpack.config.js
const path = require('path') // 路徑處理模塊
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入HtmlWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  ....
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/index.html') // new一個這個插件的實例,並傳入相關的參數
    })
  ]
}
複製代碼

6.3 清理/dist文件夾(clean-webpack-plugin)

你可能已經注意到,在咱們刪掉/dist文件夾以前,因爲前面的代碼示例遺留,致使咱們的/dist文件夾比較雜亂。webpack會生成文件,而後將這些文件放置在/dist文件夾中,可是webpack沒法追蹤到哪些文件是實際在項目中用到的。

一般,在每次構建前清理/dist文件夾,是比較推薦的作法,所以只會生成用到的文件,這時候就用到CleanWebpackPlugin插件了。 安裝:npm i clean-webpack-plugin -D

// webpack.config.js
const path = require('path') // 路徑處理模塊
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入HtmlWebpackPlugin插件
const ClearWebpackPlugin = require('clean-webpack-plugin') // 引入ClearWebpackPlugin插件
module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/src/index.template.html') // new一個這個插件的實例,並傳入相關的參數
    }),
    new ClearWebpackPlugin(['dist']) // 清理所要清理的文件夾名稱
  ]
}
複製代碼

插件的使用方法都是同樣的,首先引入,而後new一個實例,實例可傳入參數。 如今咱們運行npm run build後就會發現,webpack會先將/dist文件夾刪除,而後再生產新的/dist文件夾。

6.4 熱更新(HotModuleReplacementPlugin)

HotModuleReplacementPlugin(HMR)是一個很實用的插件,能夠在咱們修改代碼後自動刷新預覽效果。

方法:

  • (1):devServer配置項中添加hot: true參數。
  • (2):由於HotModuleReplacementPlugin是webpack模塊自帶的,因此引入webpack後,在plugins配置項中直接使用便可。
// webpack.config.js
const path = require('path') // 路徑處理模塊
const webpack = require('webpack') // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入HtmlWebpackPlugin插件
const ClearWebpackPlugin = require('clean-webpack-plugin') // 引入ClearWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  ...
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/src/index.template.html') // new一個這個插件的實例,並傳入相關的參數
    }),
    new ClearWebpackPlugin(['dist']), // 清理所要清理的文件夾名稱
    new webpack.HotModuleReplacementPlugin() // 熱更新插件
  ]
}
複製代碼

此時咱們從新啓動項目npm run dev後,修改hello.js的內容,會發現瀏覽器預覽效果會自動刷新(也許反應會比較慢,由於咱們使用了source-map和其餘配置的影響,後面代碼分離的時候咱們再處理)。

7 項目優化及拓展

7.1 代碼分離

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

  • 根目錄下新建webpack.common.js(公共配置文件)、webpack.dev.js(開發環境配置文件)、webpack.prod.js(生產環境配置文件)

安裝一個合併模塊插件webpack-merge

npm i webpack-merge -D
複製代碼
  • 拆分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是從右往左編譯的
      },
      {
        test: /\.(js|jsx)$/,
        use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
          loader: 'babel-loader'
          // options: { // 後續Babel配置會單獨提取到.babelrc文件中
          //   presets: [ 'env' ] // 支持最新JS語法(ES六、ES七、ES8。。。)
          // }
        },
        exclude: /node_modules/ // 排除匹配node_modules模塊
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/src/index.template.html') // new一個這個插件的實例,並傳入相關的參數
    }),
    new webpack.HotModuleReplacementPlugin() // 熱更新插件
  ]
}
複製代碼
// webpack.prod.js
const ClearWebpackPlugin = require('clean-webpack-plugin') // 引入ClearWebpackPlugin插件
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  plugins: [
    new ClearWebpackPlugin(['dist']) // 清理所要清理的文件夾名稱
  ]
})
複製代碼
// webpack.dev.js
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
  // 本地服務器配置
  devServer: {
    contentBase: './', // 本地服務器所加載文件的入口
    port: '8080', // 設置端口號,若是省略,默認爲8080
    inline: true, // 設置爲true,當源文件改變時會自動刷新頁面
    historyApiFallback: false, // 設置爲true,全部的跳轉將指向index.html
    hot: true // 熱加載
  }
})
複製代碼
  • 設置package.json的scripts命令:
{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config src/webpack.dev.js",
    "test": "npm run test"
  },
  ...
}
複製代碼

咱們把build命令改成了webpack --config webpack.prod.js,意思是把打包配置指向webpack.prod.js配置文件,而以前咱們只須要使用一個webpack命令爲何就能夠運行了?由於webpack命令是默認指向webpack.config.js這個文件名稱了,如今咱們把文件名稱改了,因此就須要自定義指向新的文件,dev命令中的指令也同理。 而後咱們運行npm run build和npm 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 增長css前綴、分離css、消除冗餘css、分離圖片

  1. 增長css前綴postcss-loaderautoprefixer

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

npm i postcss-loader autoprefixer -D
複製代碼

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

module.exports = {
  plugins: [
    require('autoprefixer') // 引用autoprefixer模塊
  ]
}
複製代碼

index.css中增長如下樣式

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

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

// 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: [
          { loader: 'style-loader' }, // 這裏採用的是對象配置loader的寫法
          { loader: 'css-loader' },
          { loader: 'postcss-loader' }// 使用postcss-loader
        ] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      ...
    ]
  }
}
複製代碼

而後咱們運行npm run dev後css樣式中會自動添加前綴

  1. 分離css插件extract-text-webpack-plugin

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

npm i extract-text-webpack-plugin@next -D  // 加上@next是爲了安裝最新的,不然會出錯
複製代碼

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

// webpack.common.js
const path = require('path') // 路徑處理模塊
const webpack = require('webpack') // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入HtmlWebpackPlugin插件
const ExtractTextPlugin = require('extract-text-webpack-plugin') // 引入分離插件
module.exports = {
  entry: path.join(__dirname, '/src/index.js'), // 入口文件
  output: {
    path: path.join(__dirname, '/dist'), // 打包後的文件存放的地方
    filename: 'bundle.js' // 打包後輸出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 正則匹配以.css結尾的文件
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            { loader: 'css-loader' },
            { loader: 'postcss-loader' }// 使用postcss-loader
          ] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
        })
      },
      ...
    ]
  },
  plugins: [
    ...
    new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css
  ]
}
複製代碼

7.3 消除冗餘css插件 purifycss-webpackpurify-css

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

npm i purifycss-webpack purify-css glob -D
複製代碼

安裝完上述三個模塊後,由於正常來講是在生產環境中優化代碼,因此咱們應該是在webpack.prod.js文件中進行配置,引入clean-webpack-plugin及glob插件並使用它們:

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

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 ClearWebpackPlugin(['dist']), // 清理所要清理的文件夾名稱
    new PurifyCssWebpack({
      paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步掃描全部html文件中所引用的css
    })
  ]
})
複製代碼

在index.css文件中增長一些多餘的代碼測試:

body {
    background: gray;
}
#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代碼的:

body {
  background: gray;
}

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

7.4 處理圖片 url-loader

處理圖片須要安裝兩個loader:

雖然咱們只需使用url-loader,但url-loader是依賴於file-loader的,因此也要安裝

npm i url-loader file-loader -D
複製代碼

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

// webpack.common.js
...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(png|jpg|svg|gif)$/, // 正則匹配圖片格式
        use: [
          {
            loader: 'url-loader' // 使用url-loader
          }
        ]
      }
    ]
  },
  ...
}
複製代碼

更新index.css, 背景改成背景

body {
    background: gray;
    background-image: url(../images/back.png) top right repeat-y;
}
...
複製代碼

運行npm run dev後,背景圖片變成了base64,由於webpack會自動優化圖片,減小發送請求,可是若是我想把它變成路徑的該怎麼作? 能夠把webpack.common.js的loader配置更改一下,增長options選項:

// webpack.common.js
...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(png|jpg|svg|gif)$/, // 正則匹配圖片格式
        use: [
          {
            loader: 'url-loader', // 使用url-loader
            options: {
              limit: 1000 // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲384kb,因此不會被轉化
            }
          }
        ]
      }
    ]
  },
  ...
}
複製代碼

而後咱們運行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', // 使用url-loader
            options: {
              limit: 1000, // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲384kb,因此不會被轉化
              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({ // 這裏咱們須要調用分離插件內的extract方法
          fallback: 'style-loader', // 至關於回滾,經postcss-loader和css-loader處理過的css最終再通過style-loader處理
          use: [
            { loader: 'css-loader' },
            { loader: 'postcss-loader' }// 使用postcss-loader
          ], // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
          publicPath: '../' // 給背景圖片設置一個功能路徑
        })
      },
      {
        test: /\.(png|jpg|svg|gif)$/, // 正則匹配圖片格式
        use: [
          {
            loader: 'url-loader', // 使用url-loader
            options: {
              limit: 1000, // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲384kb,因此不會被轉化
              outputPath: 'images' // 設置打包後圖片存放的文件夾名稱
            }
          }
        ]
      }
    ]
  },
  ...
}
複製代碼

7.5 壓縮代碼

在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",
    "test": "npm run test"
  },
  ...
}
複製代碼

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

8 根據運行環境變換對應的IP

8.1 安裝cross-env修改生產環境變量

項目背景:項目有三個分支、dev(開發分支)、uat(測試環境)、prod(上線環境) 如今須要:運行對應的命令npm run dev:dev、uat、prod以及npm run build:dev、uat、prod會調用對用的host

步驟以下

  • 一、cross-env能跨平臺地設置及使用環境變量,安裝npm i --save-dev cross-dev
  • 二、config文件夾下新建dev.js(開發環境)、prod.js(生產環境)。配置文件以下
// dev.js
// 在任何文件裏都能簡單的用下面代碼獲取到配置
// const NODE_ENV = process.env.NODE_ENV
// const BRANCH = process.env.BRANCH
module.exports = {
  NODE_ENV: "'development'", // 開發模式|生產模式
  /*
   * 一、process.env.BRANC 讀取終端執行的npm命令
   * 二、BRANCH: JSON.stringify(process.env.BRANCH) || 'dev':用於接受npm命令的修改
   * 三、默認dev
   */
  BRANCH: JSON.stringify(process.env.BRANCH) || "'dev'"
}
複製代碼
// prod.js
// 在任何文件裏都能簡單的用下面代碼獲取到配置
// const NODE_ENV = process.env.NODE_ENV
// const BRANCH = process.env.BRANCH
module.exports = {
  NODE_ENV: "'production'", // 開發模式|生產模式
  /*
   * 一、process.env.BRANC 讀取終端執行的npm命令
   * 二、BRANCH: JSON.stringify(process.env.BRANCH) || 'dev':用於接受npm命令的修改
   * 三、默認dev
   */
  BRANCH: JSON.stringify(process.env.BRANCH) || "'dev'"
}
複製代碼
  • 三、在package.json中配置npm 腳本以下:
{
  ...
  "scripts": {
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development",
    "dev:dev": "cross-env BRANCH=dev webpack-dev-server --open --config webpack.dev.js --mode development"
    ...
  },
  ...
}
複製代碼
  • 四、在webpack.dev.js中引入dev.js,webpack.prod.js引入prod.js 並將其設置爲全局變量,具體以下
// webpack.dev.js
const dev = require('./config/dev')
...
module.exports = merge(common, {
  ...
  plugins: [
    new webpack.DefinePlugin({ // DefinePlugin能夠在編譯時期建立全局變量。
      'process.env': dev
    })
  ]
})
複製代碼
// webpack.prod.js
...
const dev = require('./config/prod')
module.exports = merge(common, { // 將webpack.common.js合併到當前文件
  plugins: [
    ...
    new webpack.DefinePlugin({ // DefinePlugin能夠在編譯時期建立全局變量。
      'process.env': dev
    })
  ]
})
複製代碼
  • 五、 config文件下新建common.js,用於存放公共的方法,並配置
// common.js
module.exports = {
  /**
   * [getHost 根據執行腳本的具體命令,返回具體的請求IP]
   * @return {[type]} [description]
   */
  getHost () {
    const BRANCH = `${process.env.BRANCH}`
    let HOST = ''
    switch (BRANCH) {
      case 'dev' :
        HOST = 'https://xxx.com'
        break
      default :
        HOST = ''
    }
    return HOST
  }
}
複製代碼
  • 六、 分別運行npm run build npm run dev使用. 就能夠看到效果
// index.js
const { post } = require('./preset/request')
const promise = post({
  url: '/xxx/xxx',
  data: {
    tenantCode: '88000531',
    keyword: '',
    page: {
      size: 10,
      page: 1
    }
  }
})
promise.then(res => {
  console.log('res', res)
}).catch(err => {
  console.log('err', err)
})
複製代碼

運行npm run build:dev,這樣NODE_ENV便設置成功,無需擔憂跨平臺問題 在任何頁面使用都能獲取process.env.BRANCH

8.2 安裝 Axios

Axios具備如下特徵:

  • 從瀏覽器中建立 XMLHttpRequests
  • 從 node.js 建立 http 請求
  • 支持 Promise API 攔截請求和響應 轉換請求數據和響應數據 取消請求 自動轉換 JSON 數據 客戶端支持防護 XSRF

安裝命令:npm install axios 封裝請求的request.js

const HOST = require('../../config/common').getHost() // 獲取命令後綴
const _axios = require('axios') // 使用axios
const axios = _axios.create({ // 建立實例
  baseURL: HOST, // IP
  timeout: 5000 // 請求超時時間
})
// 配置默認值
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
/**
 * [description]
 * @param  {[type]} url     [請求地址]
 * @param  {[type]} params  [請求參數, 與url拼接的]
 * @param  {[type]} headers [請求頭]
 * @param  {[type]} timeout [超時時間]
 * @return {[type]}         [返回promise]
 */
module.exports.get = function ({ url = '', params = {}, headers = {}, timeout = 5000 }) {
  const promise = axios.get(url, { params, timeout })
  return promise
}
/**
 * [POST請求]
 * @param  {[type]} url     [請求地址]
 * @param  {[type]} data    [請求參數]
 * @param  {[type]} params  [地址拼接參數]
 * @param  {[type]} headers [請求頭]
 * @param  {[type]} timeout [超時]
 * @return {[type]}         [返回promise]
 */
module.exports.post = function ({ url = '', data = {}, params = {}, headers = {}, timeout = 5000 }) {
  const promise = axios.post(url, { data, params, headers, timeout })
  return promise
}
/**
 * [PUT請求]
 * @param  {[type]} url     [請求地址]
 * @param  {[type]} data    [請求參數]
 * @param  {[type]} params  [地址拼接參數]
 * @param  {[type]} headers [請求頭]
 * @param  {[type]} timeout [超時]
 * @return {[type]}         [返回promise]
 */
module.exports.put = function ({ url = '', data = {}, params = {}, headers = {}, timeout = 5000 }) {
  const promise = axios.put(url, { data, params, headers, timeout })
  return promise
}
/**
 * [DELETE請求]
 * @param  {[type]} url     [請求地址]
 * @param  {[type]} data    [請求參數]
 * @param  {[type]} params  [地址拼接參數]
 * @param  {[type]} headers [請求頭]
 * @param  {[type]} timeout [超時]
 * @return {[type]}         [返回promise]
 */
module.exports._delete = function ({ url = '', data = {}, params = {}, headers = {}, timeout = 5000 }) {
  const promise = axios.delete(url, { data, params, headers, timeout })
  return promise
}
複製代碼
相關文章
相關標籤/搜索