簡單介紹Webpack 的構建項目的主要環節

安裝webpack

首先安裝webpack、webpack-cli、webpack-dev-server。javascript

項目初始化

這裏使用vue項目作演示,大家能夠用別的,不影響webpack的構建。創建webpack.config.js文件css

引入一系列的依賴和loader

vue-loader和vue-template-compiler是vue必須的,官方地址:https://vue-loader.vuejs.org/zh/html

node-sass、less、css-loader、vue-style-loader、less-loader和sass-loader讓咱們項目支持css、less和sass。vue

babel-loader、@babel/core、@babel/preset-env讓咱們項目支持ES6。java

還有圖片文件和字體文件的加載器,url-loader依賴於file-loader因此都要安裝 。node

關於devServer

如其名稱,devServer是用於啓動開發環境的服務的,配置項能夠上官網看,比較好理解。這裏就提兩個重要的配置項,一是hot爲true時,能夠啓動HMR。二是proxy,在先後端分離的項目,可使用這個配置來代理後臺的服務地址。webpack

//webpack.config.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports ={
    entry:'./src/main.js', // 項目的入口文件
    output:{
        filename: 'bundle.js',    // 打包後的文件名
        path: path.join(__dirname, 'dist'),   // 項目的打包文件路徑
        publicPath: 'dist/'   // 輸出解析文件的目錄,指定資源文件引用的目錄
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: file => (
                    /node_modules/.test(file) &&
                    !/\.vue\.js/.test(file)
                )
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'   // vue官網介紹,使用這個loader的同時,還須要VueLoaderPlugin
            },
            {
                test: /\.css$/,  //匹配後綴名爲css的文件,而後分別用css-loader,vue-style-loader去解析
                use: [  //解析器的執行順序是從下往上(先css-loader再vue-style-loader)
                    'vue-style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.scss$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'sass-loader'
                ],
            },
            {
                test: /\.less$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test:/\.(png|jpe?j|gif|svg)(\?.*)?$/,
                loader:'url-loader',
                options:{
                    limit: 10 * 1024, // 10 KB  //圖片文件大小小於limit的數值,就會被改寫成base64直接填入url裏面
                }
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()  // 官方使用vue-loader時,必要的plugin
    ],
    devServer: {    // 開發服務器工具
        contentBase: './public', //基於的根目錄
        open: true,  // 自動打開瀏覽器
        overlay: true // 將錯誤顯示在html之上
    }
}

爲了之後方便啓動,把config文件配置好後,再把package.json的scripts也配置一下web

"scripts": {
    "serve": "webpack-dev-server",  //webpack5.0和webpack-cli4.0不支持webpack-dev-server,使用webpack serve
    "build": "webpack"
  }

這時啓動項目,就能夠看到編譯後的項目了。json

引入經常使用的插件

clean-webpack-plugin能夠在每次打包前把上一次打包的文件刪除再進行打包。後端

html-webpack-plugin能夠將html文件打包後自動引入打包後的js文件,能夠經過設置相關屬性來指定html,若是想要生成多個html,能夠屢次new這個插件。引入這個插件後,就不須要設置output.publicPath和devServer.contentBase屬性了。

copy-webpack-plugin將須要拷貝的文件放入打包後的文件,通常不在開發階段使用。

HotModuleReplacementPlugin熱更新插件(也稱HMR),這個插件是webpack自己提供的,不須要再單獨去下載依賴。在devServer中配置{hot:true}時啓動,css自動生效,而js等須要手動添加(使用module.hot.accept(dep,callback))

//webpack.config.js
const webpack = require('webpack')
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports ={
    entry:'./src/main.js', // 項目的入口文件
    output:{...},
    module: {...},
    plugins: [
        new VueLoaderPlugin(),  // 官方使用vue-loader時,必要的plugin
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Vue Sample',      // 設置title
            meta: {                           // 設置meta
                viewport: 'width=device-width'
            },
            template: './public/index.html'      // 指定模板
        }),
        new webpack.HotModuleReplacementPlugin(),
        // // 開發階段最好不要使用這個插件
        // new CopyWebpackPlugin(['public'])
    ],
    devServer: {   // 開發服務器工具
        hot: true,   // 啓動HMR ,若是要手動添加一些文件的HMR,能夠用hotOnly來調試
        open: true,  // 自動打開瀏覽器
        overlay: true // 將錯誤顯示在html之上
    }
}

不一樣工做環境的配置

建立不一樣環境配置的方式主要有兩種方法,一是在配置文件中添加相應的條件,根據不一樣的條件來導出相應的配置;二是根據不一樣的環境添加對應的配置文件。在不一樣的環境能夠有不一樣配置項,像mode、devtool等,還有上面用到的clean-webpack-plugin、copy-webpack-plugin等插件均可以區分到所對應的環境中。

根據不一樣的條件來導出相應的配置

這個方法主要經過cli配置的環境名參數,而後再module.exports裏接收這個參數來進行判斷,啓動production的方法能夠是在命令行輸入‘webpack --env production’,也能夠配置在scripts中。

module.exports = (env, argv) => {
  const config = {
    mode: 'development',
    entry: './src/main.js',
    output: {...},
    devtool: 'cheap-eval-module-source-map',
    devServer: {...},
    module: {...},
    plugins: [...]
  };

  if (env === 'production') {
    config.mode = 'production';
    config.devtool = false;
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])
    ];
  }

  return config;
}

根據不一樣的環境添加對應的配置文件

這個方法如其名,是要創建不一樣環境的配置文件來進行匹配。通常須要創建一個webpack.common.js一個公共配置,而後再按開發、生產的等環境在創建對應環境特有的配置文件,最後在啓動項目時經過'webpack --config webpack.prod.js'來啓動不一樣的項目便可。

// webpack.common.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports ={
    entry:'./src/main.js', // 項目的入口文件
    output:{...},
    module: {...},
    plugins: [
        new VueLoaderPlugin(),  // 官方使用vue-loader時,必要的plugin
        new HtmlWebpackPlugin({
            title: 'Webpack Vue Sample',      // 設置title
            meta: {                           // 設置meta
                viewport: 'width=device-width'
            },
            template: './public/index.html'      // 指定模板
        }),

    ],
}
//webpack.dev.js
const webpack = require('webpack');
const {merge} = require('webpack-merge');

const common = require('./webpack.common');

module.exports = merge(common,{
    mode: 'development',
    devtool: 'cheap-eval-module-source-map',
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ],
    devServer: {   // 開發服務器工具
        hot: true,   // 啓動HMR
        open: true,  // 自動打開瀏覽器
        overlay: true // 將錯誤顯示在html之上
    }
});
// webpack.prod.js
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const {merge} = require('webpack-merge');

const common = require('./webpack.common');

module.exports = merge(common,{
    mode:'production',
    plugins: [
        new CleanWebpackPlugin(),
        new CopyWebpackPlugin( ["public"])
    ],
});

配置全局常量

經過對開發模式和生產模式配置不一樣的常量,從而能夠在構建時容許不一樣的行爲。 可使用webpack自帶的DefinePlugin來進行配置。

new webpack.DefinePlugin({
    BASE_URL:JSON.stringify('./')
}),

打包過程的優化

Tree Shaking

Tree Shaking在production模式下會自動開啓,咱們想要在其餘模式下也使用的話,須要本身來配置。

optimization: {
        usedExports: true,  // 模塊只導出被使用的成員
        concatenateModules: true,  // 儘量合併每個模塊到一個函數中
        minimize: true, // 壓縮輸出結果
    },

sideEffects

sideEffects在production模式下也會自動開啓,它主要的做用是對所標識的沒有反作用文件進行識別,將沒有用的到的代碼不進行打包。想在除production模式下也啓用這個功能,能夠對webpack配置文件和package.json文件兩個文件進行配置。

// webpack配置文件
  optimization: {
        sideEffects: true, // 開啓sideEffects 功能
    },
// package.json
 "sideEffects": [
    "*.css"   //標識有反作用的文件
  ]

Code Splitting

Code Splitting主要做用就是將打包後的代碼進行分包,具體實現有兩種方法:1.多入口打包;2.動態導入;

多入口打包

多入口打包的功能主要經過webpack的配置來實現

//  多入口打包
module.exports = {
  entry: {   // 入口改成多文件導入
    index: './src/index.js',
    app: './src/app.js'
  },
  output: {
    filename: '[name].bundle.js' // 導出的文件名動態生成
  },
  module: {...},
  optimization: {
    splitChunks: {
      chunks: 'all'   // 防止重複引用,自動提取全部公共模塊到單獨 bundle
    }
  },
  plugins: [
    new HtmlWebpackPlugin({  
      title: 'index Page',
      template: './src/index.html',
      filename: 'index.html',
      chunks: ['index']  //設置html須要導入的js文件
    }),
    new HtmlWebpackPlugin({
      title: 'app Page',
      template: './src/app.html',
      filename: 'app.html',
      chunks: ['app']
    })
  ]
}

動態導入

動態導入指的是在應用運行到須要某個模塊的時候,再來導入這個模塊。webpack自動支持這種方式而且會單獨進行分包,其主要應用場景是路由切換時。打包後的文件還能夠經過魔法註釋(/* webpackChunkName: 'name' */)來定義文件的名稱

const render = () => {
  const hash = window.location.hash || '#home'

  const mainElement = document.querySelector('.main')

  mainElement.innerHTML = ''

  if (hash === '#home') {
    import(/* webpackChunkName: 'homeCom' */'./home/home').then(({ default: home}) => {
      mainElement.appendChild(home())
    })
  } else if (hash === '#index') {
    import(/* webpackChunkName: 'indexCom' */'./index/index').then(({ default: index}) => {
      mainElement.appendChild(index())
    })
  }
}

render()

window.addEventListener('hashchange', render)

css文件提取到單個文件

當css文件比較大想要單獨提取出來時,可使用‘mini-css-extract-plugin’插件,在使用這個插件時,還要同時使用‘optimize-css-assets-webpack-plugin’、'terser-webpack-plugin'這兩個插件,‘optimize-css-assets-webpack-plugin’是用來壓縮css文件的,'terser-webpack-plugin'是用來壓縮js文件的。由於在配置minimizer後,webpack會取消默認的js壓縮plugin,多以須要本身再把js的plugin給添加進來。

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {...},
  optimization: {
    minimizer: [   // webpack官方建議把壓縮性質的文件用minimize來統一管理
      new TerserWebpackPlugin(),  //  js文件壓縮
      new OptimizeCssAssetsWebpackPlugin()  // css文件壓縮
    ]
  },
  module: {
    rules: [
       ...,
      {
        test: /\.css$/,
        use: [
          // 'style-loader', // 將樣式經過 style 標籤注入
          MiniCssExtractPlugin.loader, // 替換掉'style-loader'
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    ...,
    new MiniCssExtractPlugin()  // 提取css文件到單個文件,若是單個css文件體積不大,不必單獨提取到一個文件
  ]
}

substitution

webpack提供了一個substitution(可替換的模版字符串)的方式,這個方式將根據資源內容建立出惟一 hash 。主要有三個模板:1.'hash',2.'chunckhash',3.'contenthash',具體何時用,就不作多介紹了。

new MiniCssExtractPlugin({
  filename: 'static/css/[name].[contenthash:4].css',   // hash默認20位長度,能夠用':'接數字,指定長度
  chunkFilename: 'static/css/[name].[contenthash:4].css',
}),

ESlint

使用eslint-loader做爲pre-loader運用,能夠在開發過程當中每次保存的時候就會自動進行代碼校驗。

module.exports = {
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      }
    ]
  }
}
相關文章
相關標籤/搜索