手寫一個 webpack4.0配置

2jian丶發表於2019-04-18

webpack詳解

webpack是一個打包工具,他的宗旨是一切靜態資源即可打包。有人就會問為什麼要webpack?webpack是現代前端技術的基石,常規的開發方式,比如jquery,html,css靜態網頁開發已經落後了。現在是MVVM的時代,資料驅動介面。webpack將現代js開發中的各種新型有用的技術,集合打包。webpack的描述鋪天蓋地,我就不浪費大家的時間了。理解下這種圖就知道webpack的生態圈了:

avatar

webpack4.0的配置

const path = require('path'); //引入node的path模組
const webpack = require('webpack'); //引入的webpack,使用lodash
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');//壓縮外掛
const HtmlWebpackPlugin = require('html-webpack-plugin') //引入打包我們的HTML
const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包的css,less拆分,將一部分抽離出來 
const CopyWebpackPlugin = require('copy-webpack-plugin')
const PurifyCSSPlugin = require('purifycss-webpack');
//console.log(path.resolve(__dirname,'dist')); //實體地址拼接
// console.log(encodeURIComponent(process.env.type));

if(process.env.type=="build"){  //方便快速選擇生產環境或開發環境
  var website={
    publicPath:"http://www.z-yj.com:1717/"
  }
}else{
  var website={
    publicPath:"http://192.168.31.165:1717/"
  }
}
module.exports = {
  // devtool:'eval-source-map',  //針對開發或生產的打包模式
  entry: './src/index.js', //入口檔案  在vue-cli main.js
  output: {
    path: path.resolve(__dirname, 'dist'), //定位,輸出檔案的目標路徑
    filename: '[name].js',  //檔名[name].js預設,也可自行配置
    publicPath:website.publicPath   //為配置生產,或開發模式提供引數
  },
  module:{      //模組的相關配置
    rules:[        //根據檔案的字尾提供一個loader,解析規則
      {
        test: /\.(htm|html)$/i,
        use:[ 'html-withimg-loader'] 
      },
      { 
        test:/\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: [
            {loader:"css-loader",options:{importLoaders:1} },
            'postcss-loader'
        ]
        })
        // use:['style-loader','css-loader'],//use/loader 一樣的
        // use:[{
        //   loader:"style-loader",
        //   module:true
        // },{
        //   loader:"css-loader"
        // }]
        //include手動配置那些檔案 exclude那些檔案不需要處理 query為loader提供額外的配置項
      },
      {
        test:/\.(jsx|js)$/, //es6 => es5 
        include: [
          path.resolve(__dirname,'src')
        ],
        // exclude:[], 不匹配選項(優先順序高於test和include)
        use: 'babel-loader'
      },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback:'style-loader',
          use: [
            'css-loader',
            'less-loader'
          ]
        })
      },
      {        //圖片loader
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader' //根據檔案地址載入檔案
          }
        ]
      }
    ]
  },
  resolve:{  //解析模組的可選項
    // modules: [ ]//模組的查詢目錄 配置其他的css等檔案
    extensions:[".js",".json",".jsx",".less",".css"],   //用到檔案的副檔名
    alias:{  //模快別名列表
      utils:path.resolve(__dirname,'src/utils')
    }
  },
  // optimization: {   //自定義一些優化構建打包的策略配置
  //   splitChunks: {
  //       cacheGroups: {
  //           commons: {
  //               name: true,
  //               // filename:"assets/js/[name].[ext]",
  //               chunks: "initial",
  //               minChunks: 2
  //           }
  //       }
  //   }
  // },
  plugins:[  //插進的引用, 壓縮,分離美化
    new UglifyJsPlugin(),//壓縮外掛
    new ExtractTextPlugin('[name].css'),   //[name] 預設  也可以自定義name  宣告使用
    new HtmlWebpackPlugin({  //將模板的頭部和尾部新增css和js模板,dist 目錄釋出到伺服器上,專案包。可以直接上線
      file:'index.html',  //打造單頁面運用 最後執行的不是這個
      template:'src/index.html' //vue-cli放在根目錄下
      // minify:{
      //   removeAttributeQuotes:true//去掉html的屬性引號 如:id=title,
      // },
      // hash:true,//解除快取cache,如:打包後index.html,引入的檔案後會有一串雜湊值,用來防止快取 
    }),
    new CopyWebpackPlugin([{  //src下其他的檔案直接複製到dist目錄下
      from:'src/assets/favicon.ico',
      to:'favicon.ico'
    }]),
    new PurifyCSSPlugin({   //消除無用的css
      paths:glob.sync(path.join(__dirname,'src/*.html'))
    }),
    new webpack.ProvidePlugin({  //引用框架 jquery  lodash工具庫是很多元件會複用的,省去了import
      $:"jquery",
      '_':'lodash'  
    }),
    new webpack.BannerPlugin('ZYJian版權所有,詳情到z-yj.com學習!'),
  ],
  //配置服務
  devServer:{ //在webpack.config.js裡配置port
    port:8080,
    contentBase:path.resolve(__dirname,'dist'),
    host:'192.168.31.165',
    compress:true,//伺服器壓縮
    // before(app){
    //   app.get('/api/test.json',(req,res) => {
    //     res.json({
    //       code:200,
    //       message: 'hello World'
    //     })
    //   })
    // }
    //服務於webpack-dev-server  內部封裝了一個express 
  },
  // watchOptions:{   //開啟監聽
  //   poll:1000,
  //   aggregateTimeout:500,
  //   ignored:/node_modules/,
  // }
 
}

複製程式碼

一、前端環境搭建

我們使用npm或yarn來安裝webpack。

npm install webpack webpack-cli -g 
# 或者
yarn global add webpack webpack-cli
複製程式碼

為什麼webpack會分為兩個檔案呢?在webpack3中,webpack本身和它的cli以前都是在同一個包中,但在第4版中,他們已經將兩者分開來更好地管理它們。 新建一個webpack的資料夾,在其下新建一個try-webpack(防止init時專案名和安裝包同名)並初始化和配置webpack。

 npm init -y  //-y 預設所有的配置
 yarn add webpack webpack-cli -D  //-D webpack安裝在devDependencies環境中
複製程式碼

二、部署webpack

在上面搭建好的環境專案中,我們來到package.json裡配置我們的scripts。

  "scripts": {
    "server": "webpack-dev-server --open",
    "watch": "webpack --watch --mode development",
    "dev": "set type=dev&webpack --mode development",
    "build": "set type=build&webpack --mode production",
    "dev_mac": "export type=dev&&webpack --mode development",
    "build_mac": "export type=build&&webpack --mode production"
  },
  "devDependencies": {
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0",
  }
複製程式碼

配置好我們webpack的執行環境時,聯想下vue-cli。平時使用vue-cli會自動幫我們配置並生成專案。我們在src下進行專案的開發,最後npm run build 打包生成我們的dist的目錄。不知道你是否還記得,還是讓我們進入下一節讓我們感受下這其中的正個流程吧。

三、npm run build 發生了什麼

在我們的根專案下try-webpack新建一個src目錄。在src目錄下新建一個index.js檔案。在裡面我們可以寫任意的程式碼,以案例為主:

  const a = 1;
複製程式碼

寫完之後我們在終端執行我們的命令: npm run build ,你就會發現新增了一個dist目錄,裡面存放著webpack打包好的main.js檔案。這和我們在vue-cli裡操作是一樣的。

四、webpackp配置流程篇

我們在開發是一般會打包src下的什麼檔案呢?我們可以回憶一下,其實vue-cli專案src下不就這幾點嘛:

  • 釋出時需要的html,css,js
  • css預編譯器stylus,less,sass
  • es6的高階語法
  • 圖片資源.png,.gif,.ico,.jpg
  • 檔案間的require
  • 別名@等修飾符

那麼我將會分這幾點來講解webpack中webpack.config.js的配置,跟著腳步,一步一步的來完成我們的流程線

✍️Html在webpack中的配置

在專案的根目錄try-webpack下新建webpack.config.js檔案,以commonJS模組化機制向外輸出,並且新建一個index.html。

module.exports = {}
複製程式碼

配置我們的入口entry,在vue-cli裡相當於跟目錄下的main.js,我們的出口output。我們可以把webpack理解為一個工廠,進入相當於把各種各樣的原料放進我們的工廠了,然後工廠進行一系列的打包操作把打包好的東西,向外輸出,然後就可以去出售了(上線)。

const path = require('path'); //引入node的path模組
//測試下 console.log(path.resolve(__dirname,'dist')); //實體地址拼接
module.exports = {
  entry: './src/index.js', //入口檔案  在vue-cli main.js
  output: {
    path: path.resolve(__dirname, 'dist'), //定位,輸出檔案的目標路徑
    filename: '[name].js',  //檔名[name].js預設,也可自行配置
  },
複製程式碼

HTML打包我們需要安裝引入html-webpack-plugin

yarn add html-webpack-plugin -D //在開發環境中安裝
const HtmlWebpackPlugin = require('html-webpack-plugin') ////引入打包我們的HTML
複製程式碼

在module.exports裡配置我們的plugins(外掛):

plugins:[  //插進的引用, 壓縮,分離美化
  new HtmlWebpackPlugin({  //將模板的頭部和尾部新增css和js模板,dist 目錄釋出到伺服器上,專案包。可以直接上線
      file:'index.html',  //打造單頁面運用 最後執行的不是這個
      template:'src/index.html' //vue-cli放在根目錄下
      // minify:{
      //   removeAttributeQuotes:true//去掉html的屬性引號 如:id=title,
      // },
      // hash:true,//解除快取cache,如:打包後index.html,引入的檔案後會有一串雜湊值,用來防止快取 
    }),
],
複製程式碼

配置好後,在終端輸入npm run dev後webpack將我們的html打包好並且自動將我們的js引進來了。

<body>
    <p class="main">Hello World</p>
<script type="text/javascript" src="main.js"></script>
</body>
複製程式碼

live-sever我們的dist目錄,啟動一個8080埠,我們就可以看到我們的Hello World了。這就是我們上線版的頁面。

?css在webpack中的配置

在我們vue-cli裡,我們可以使用css去寫我們的樣式,也可以使用高階stylus,less,sass等預編譯器。這裡就以less為例,看看webpack怎麼將他打包成一個css。

.main {
  color: red;
}
複製程式碼

在src目錄下新建我們的style.less檔案,在配置之前我們需要npm我們的css-loader和sass-loader,sass:

yarn add css-loader less less-loader style-loader-D
複製程式碼

執行完上述命令我們在packge.json裡可以看到我們的配置檔案:

"devDependencies": {
    "css-loader": "^2.1.1",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0",
  }
複製程式碼

安裝好後,我們開始配置webpack.config.js檔案。這裡申明一下,我們的css在dist目錄下需要和我們的HTML分離,這是還需引入我們的extract-text-webpack-plugin,先然我們安裝下:

yarn add extract-text-webpack-plugin-D
複製程式碼

這裡有一個坑,extract-text-webpack-plugin在4.0並不支援這樣的安裝,大家可自行chrome。於是我們選擇換一種方式,選擇4.00-beta.0版本的:

yarn add extract-text-webpack-plugin@last-D
複製程式碼

來到我們的module.exports裡,完成moudel的配置:

const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包的css,less拆分,將一部分抽離出來 
module:{      //模組的相關配置
    rules:[        //根據檔案的字尾提供一個loader,解析規則
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback:'style-loader',
          use: [
            'css-loader',
            'less-loader'
          ]
        })
      },
  ]},
plugins:[
    new ExtractTextPlugin('[name].css'),   //[name] 預設  也可以自定義name  宣告使用  
]
複製程式碼

我們在執行我們的 npm run build 之後並沒有我們的css,為什麼呢?原來在webpack配置裡css in js。意思是在打包是我們的css是打包在我們的js裡的,所有我們引入了extract-text-webpack-plugin外掛將css從裡面剝離出來。不過又一個問題,require的機制?

在我們打包過程中,檔案的引用require 按照順序來打包,這就是檔案依賴的機制。 打包好後我們在live-server,發現我們的樣式也上去了,並且css部分分離出來了。

?js在webpack中的配置

現在隨著es6的普及,越來越多的程式碼使用es6了,但是很多瀏覽器並不支援es6,比如async/awiat,const。因此需要我們引用babel來把我們es6的程式碼編譯為es5。在跟目錄下新建.babelrc,簡單配置下:

"presets": ["env"]
複製程式碼

安裝我們的babel並在webpack.config.js裡module/rules下進行配置:

yarn add babel-loader babel-core  abel-preset-env -D  //babel基本的三個檔案
{
  test:/\.(jsx|js)$/, //es6 => es5 
  include: [
    path.resolve(__dirname,'src')
  ],
  // exclude:[], 不匹配選項(優先順序高於test和include)
  use: 'babel-loader'
},
複製程式碼

?圖片資源在webpack中的配置

在src目錄下新建一個assets檔案,裡面放置幾張圖片。安裝file-loader根據檔案地址載入檔案:

yarn add file-loader -D
複製程式碼

在webpack.config.js裡module/rules:

{        //圖片loader
  test: /\.(png|jpg|gif)$/,
  use: [
    {
      loader: 'file-loader' //根據檔案地址載入檔案
    }
  ]
}
複製程式碼

?別名(@)在webpack中的配置

在src/index.js裡我們引入:

const format = require('utils/format')  // utils ?  沒有相對路徑  回想@  => 別名
在src新建相應的檔案。在format.js裡接受一個引數並把它轉成大寫
module.exports = function format(chars) {
  return chars.toUpperCase()
}
複製程式碼

在webpack中如何配置我們的別名呢?在vue-cli中我們經常@一個資料夾,其意思就是在src目錄下,現在我們去一探究竟。在module下,注意跟rules同級。

resolve:{  //解析模組的可選項
    // modules: [ ]//模組的查詢目錄 配置其他的css等檔案
    extensions:[".js",".json",".jsx",".less",".css"],   //用到檔案的副檔名
    alias:{  //模快別名列表
      utils:path.resolve(__dirname,'src/utils')
    }
  },
複製程式碼

?其他一些靜態資源在webpack中的配置

  • src下其他的檔案直接複製到dist目錄下,並不是每個檔案都需要打包處理的,很多資源可能直接就可以複製過去。使用我們的 CopyWebpackPlugin外掛
  • 引用框架 jquery lodash工具庫是很多元件會複用的,省去了import。使用webpack.ProvidePlugin外掛

五、npm run dev 發生了什麼

在vue-cli中我們啟動監聽npm run dev可以時時監控我們src下檔案的改動,那他到底發生了什麼呢。在webpack裡其實建立了一個node程式,通過webpack-dev-server其內部封裝了一個node的express模組,其配置項如下:

"scripts": { //在package.json裡宣告下使用指令碼 npm run dev    
"build": "webpack --mode production",    
"start": "webpack-dev-server --mode development"  
},
devServer:{   //服務於webpack-dev-server  內部封裝了一個express 
    port:8080,
    before(app){
      app.get('/api/test.json',(req,res) => {
        res.json({
          code:200,
          message: 'hello World'
        })
      })
    }
  },
複製程式碼

總結丶

webpack裡面博大精深,這只是剛入門。要研究其裡面真正的東西,並非一朝一夕。tapable、webpack的生命週期等等,只有理解更多,你才能飛的更遠。附上我的github地址:github原始檔地址

相關文章