webpack模塊打包 使用 構(gòu)建

上一篇主要說了webpack處理ES Module 模塊引入方式的打包
其實webpack也支持其他的規(guī)范打包如CommonJs,CMD,AMD...

npx 會自動查找當(dāng)前依賴包中的可執(zhí)行文件为障,如果找不到部服,就會去 PATH 里找拗慨。如果依然找不到,就會幫你安裝声功。
上一篇說到的宠叼,npx webpack index.js 意思就是npx幫你找到可執(zhí)行的webpack然后去打包index.js冒冬,當(dāng)然,我們也可以通過簡單的配置webpack.config.js通過配置script命令去執(zhí)行你的打包命令剂邮,配置好后挥萌,只要執(zhí)行npm run build就可以了枉侧,代碼如下

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

package.json

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "webpack-cli": "^3.3.12",
    "webpack": "^4.44.0"
  },
  "devDependencies": {},
  "scripts": {
    "build": "webpack"
  },
  "author": "",
  "license": "ISC"
}

webpack-cli 是webpack提供的一個node工具鏈榨馁,從而是我們可以直接執(zhí)行webpack npx 等翼虫,當(dāng)你不寫配置文件的時候,webpack 會執(zhí)行他的內(nèi)部打包默認配置蛙讥。webpack.config.js是默認寫法锯蛀。假如你把配置文件寫成config.js,那么你在執(zhí)行打包命令的時候次慢,就需要這么寫webpack --config config.js告訴webpack他的打包配置文件是config.js

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
你會發(fā)現(xiàn)打包的時候有這樣的警告旁涤,大概意思是,讓你配置mode(環(huán)境) development(代碼沒有被壓縮方便調(diào)試) 或者 production(代碼被壓縮)迫像,接下來我們配置一下

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

執(zhí)行webpack 打包的時候輸出如下

Hash: 84bea18a4a1fab7a5786       本次打包的唯一hash
Version: webpack 4.44.0                  使用webpack的版本
Time: 73ms                                      本次打包總共耗時
Built at: 2020/07/28 下午4:24:30        編譯完日期
    Asset      Size  Chunks             Chunk Names      輸出的一下表格目錄結(jié)構(gòu)  編譯出的文件、大小闻妓、文件的id菌羽、編譯的文件名
bundle.js  6.08 KiB    main  [emitted]  main          
Entrypoint main = bundle.js
[./src/content.js] 179 bytes {main} [built]
[./src/footer.js] 176 bytes {main} [built]
[./src/header.js] 176 bytes {main} [built]
[./src/index.js] 135 bytes {main} [built]

webpack 默認只認識js 文件,當(dāng)引入其他文件的時候該怎么打包呢由缆,舉個例子注祖,打包圖片,首先安裝npm install file-loader -D然后配置module就可以打包圖片了猾蒂,配置如下

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,    // i 正則,代表結(jié)尾是晨, 以.png .jpg等結(jié)尾的文件肚菠,同樣可以配置 txt excel 等所有文件 
        use: 'file-loader',
          options: {
            name: '[name]_[hash].[ext]',        // 圖片重命名
            outputPath: 'images/'                  // 輸入文件目錄
          }
      }
    ]
  }
}

url-loader也可以實現(xiàn)圖片打包,可以替代file-loader

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048    //  `url-loader` 功能類似于 [`file-loader`](https://github.com/webpack-contrib/file-loader)罩缴,但是在文件大形梅辍(單位 byte)低于指定的限制時,可以返回一個 DataURL箫章,小于轉(zhuǎn)成base64打包到j(luò)s里反之輸出文件
          }
        }
      }
    ]
  }
}

打包css樣式

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.css$/i,
        use: [ 'style-loader', 'css-loader' ]   // css-loader會分析用到了幾個css把所有的css合并成一個css烙荷, 'style-loader',會把合并好的css掛載到head標(biāo)簽里,執(zhí)行順序檬寂,從下到上终抽,右到左
      }
    ]
  }
}

scss的使用npm install sass-loader node-sass webpack --save-dev

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.s[ac]ss$/i,
        use: [ 'style-loader', 'css-loader', 'sass-loader' ]
      }
    ]
  }
}

做css瀏覽器兼容,這時候就得需要postcss-loader了npm i -D postcss-loader,npm i autoprefixer -Dpostcss-loader插件實現(xiàn)自動配置私有前綴焰薄,

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: true   // 開啟模塊化,這樣樣式就用作用域扒袖,就可以在代碼中怎么引入`import style from './index.scss';`
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      }
    ]
  }
}

然后創(chuàng)建postcss.config.js引用loader插件塞茅,postcss.config.js查找規(guī)則由當(dāng)前文件向外層文件查找,找到停止季率,所以可以在不同文件中配置不用的配置

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

最后結(jié)合package.json中的browserslist 做樣式兼容

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "webpack-cli": "^3.3.12"
  },
  "devDependencies": {
    "autoprefixer": "^9.8.5",
    "css-loader": "^4.0.0",
    "file-loader": "^6.0.0",
    "node-sass": "^4.14.1",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^9.0.2",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "webpack": "^4.44.0"
  },
  "scripts": {
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "browserslist": [
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]
}

字體文件也可以通過file-loader配置

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

plugin的簡單使用野瘦,如:
html-webpack-plugin 會在打包結(jié)束后,自動生成一個html文件飒泻,并把打包生成的js自動引入到這個html中

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [new HtmlWebpackPlugin()],
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

loader是什么鞭光,webpack不能識別非js結(jié)尾的文件,所以就需要loader告訴webpack怎么去打包編譯,而plugin泞遗,更像是一個生命周期函數(shù)惰许,他可以在webpack運行到某個時刻的時候,幫你做一些事情

clean-webpack-plugin使用

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin()    // 默認清理output.path中的文件
  ],
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

多個打包入口的配置史辙,配置多個打包入口的時候輸出文件的名字就不可以寫死了汹买,可以使用動態(tài)占位符,[name] [hash]等等聊倔,html-webpack-plugin也會自動幫你引入打包出來的所有js晦毙,同時你也可以給output配置參數(shù),如publicPath輸出文件的目錄耙蔑,域名等

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  output: {
    publicPath: 'http://www.cdn.cn',
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

sourceMap的配置
sourceMap,會在當(dāng)你打包出來的文件報錯的時候见妒,幫你找到對應(yīng)代碼的映射關(guān)系,從而準(zhǔn)確的定位到源代碼里面的錯誤在哪里
開發(fā)環(huán)境推薦配置 devtool: 'cheap-module-eval-source-map' 這種打包速度快甸陌,同時錯誤提示比較全
生產(chǎn)環(huán)境推薦配置 devtool: 'cheap-module-source-map', 便于調(diào)試须揣,當(dāng)然你也可以配置成none 關(guān)閉映射盐股。
cheap 只提示多好行出錯了,不提示多少列返敬。module除了業(yè)務(wù)代碼遂庄,一些loader里面的錯誤也報一下。 source-map自動生成.map文件劲赠。inline映射關(guān)系打包在代碼里 涛目。eval把對應(yīng)的代碼和source-map一起執(zhí)行,提高打包效率
參考文檔

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射凛澎,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  output: {
    publicPath: './',
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

output.library和output.libraryTarget屬性可能大家都會比較陌生霹肝,因為一般如果只在項目中使用 webpack 不需要關(guān)注這兩個屬性,但是如果是開發(fā)類庫塑煎,那么這兩個屬性就是必須了解的沫换。

umd

(function webpackUniversalModuleDefinition(..){..})(..)

amd

define(['demo'], function(demo) {
  demo();
});

commonjs

const demo = require('demo');
demo();

es module

import demo from 'demo';
demo();

大家思考一下,為什么這個類庫能支持不同方式的引入最铁?如何實現(xiàn)的讯赏?這就是 webpack 配置output.library和output.libraryTarget提供的功能。

熱更新
webpack --watch 最簡單的

webpack-dev-server

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射冷尉,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true
  },
  plugins: [
    new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'src/index.html'
    })
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}
運行 "start": "webpack-dev-server ",

使用webpack-dev-middleware實現(xiàn)一個webpack-dev-server漱挎, 在node中使用webpack

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true
  },
  plugins: [
    new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'src/index.html'
    })
  ],
  output: {
    publicPath: '/',        // 指向跟目錄雀哨。對應(yīng)server.js   一個node服務(wù) 
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

server.js

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config');
const complier = webpack(config);  // 返回一個編譯器

const app = express();
app.use(webpackDevMiddleware(complier, {
  publicPath: config.output.publicPath
}))

app.listen(3000, () => {
  console.log('server in running!!!');
})

運行 "middleware": "node server.js"

Hot Module Replacement 熱模塊替換HMR磕谅,比如我更改了頁面的樣式,webpack檢測到代碼變化雾棺,會給我們重新打包編譯膊夹,導(dǎo)致,頁面重新加載捌浩,然而這并不是我們想要的放刨,我們就可以通過HMR進行熱模塊替換,dom不變只改變樣式尸饺,不觸發(fā)所有文件的重新打包宏榕。
當(dāng)你使用了HMR的時候,樣式改變不會重新加載整個html侵佃。只會替換css麻昼。當(dāng)多個js模塊引用,其中一個js模塊發(fā)生改變的時候馋辈,這個這么寫抚芦,從而只更新這一個模塊。比如在寫vue項目中會有這樣類似的功能但是我們代碼中并沒有寫類似module.hot.accept這樣的代碼,這是因為vue-loader已經(jīng)替我們做了這一步

if(module.hot) {
  module.hot.accept('監(jiān)聽變化的模塊地址', () => { 
    代碼發(fā)生改變時所執(zhí)行的事 
  })
}
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射叉抡,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,
    hot: true,
    hotOnly: true
  },
  plugins: [
    new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'src/index.html'
    }),
    new webpack.HotModuleReplacementPlugin() 
  ],
  output: {
    publicPath: '/',        // 指向跟目錄尔崔。對應(yīng)server.js   一個node服務(wù) 
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      }
    ]
  }
}

使用babel處理ES6+語法
npm install --save-dev babel-loader @babel/core babel-loade把babel和webpack建立連接,通信褥民,@babel/core是babel的核心包
npm install @babel/preset-env --save-dev @babel/preset-env是轉(zhuǎn)化為es5代碼季春,里面包含了所有es6+轉(zhuǎn)換成es5的翻譯規(guī)則。
npm install --save @babel/polyfill 對低版本瀏覽器的語法兼容消返,全局引入import "@babel/polyfill";然后在webpack中配置按需加載
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime npm install --save @babel/runtime-corejs2

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射载弄,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,
    hot: true,
    hotOnly: true
  },
  plugins: [
    new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'src/index.html'
    }),
    new webpack.HotModuleReplacementPlugin() 
  ],
  output: {
    publicPath: '/',        // 指向跟目錄。對應(yīng)server.js   一個node服務(wù) 
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      },
      { test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {   // options里面的內(nèi)容也可以都提出去撵颊,提到根目錄下的.babelrc,就不需要在這里寫了
          // presets: [["@babel/preset-env", {
          //   targets: {
          //     chrome: "58",               // 如果chrome 58版本支持宇攻,就不去轉(zhuǎn)換了
          //     ie: "11"
          //   },
          //   useBuiltIns: "usage",     // 會幫你自動引入 @babel/polyfill
          //   "corejs": "3"
          // }]]
          plugins: [["@babel/plugin-transform-runtime", {  // 在寫類庫的時候可以這么配置,避免全局引入@babel/polyfill從而造成數(shù)據(jù)污染倡勇,此方式會以閉包等注入有作用域逞刷,不會污染到其他地兒
            "absoluteRuntime": false,
            "corejs": 2,    //  當(dāng)頁面需要轉(zhuǎn)換語法的時候幫你裝換
            "helpers": true,
            "regenerator": true,
            "useESModules": false,
            "version": "7.0.0-beta.0"
          }]]
        }
      }
    ]
  }
}

安裝react打包需要的環(huán)境
npm i react react-dom --save npm install --save-dev @babel/preset-react

react 代碼

import React, { Component } from 'react';
import ReactDom from 'react-dom';

class App extends Component {
  render() {
    return <div>hello react</div>
  }
}

ReactDom.render(<App/>, document.getElementById('app'));

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

Tree Shaking

Tree Shaking 只支持ES Module的引入方式(底層靜態(tài)引入,而CommonJS引入屬于動態(tài)引入妻熊,tree shaking只支持靜態(tài)引入)夸浅,他會把,你項目中沒有用到的模塊剔除掉扔役,mode: 'production'此環(huán)境自動就配置了

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',    // 參打開代碼映射帆喇,參數(shù)none 關(guān)閉
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,
    hot: true,
    hotOnly: true   // 如果沒有寫HMR 熱模塊相關(guān)的代碼,可以把此配置去掉厅目,自動刷新瀏覽器
  },
  plugins: [
    new CleanWebpackPlugin(['dist'],{
      root: path.resolve(__dirname, '/),   // 設(shè)置要刪除文件的根目錄
       cleanStaleWebpackAssets: false 
    }),
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'src/index.html'
    }),
    new webpack.HotModuleReplacementPlugin()   // HMR 熱模塊替換
  ],
  // 在 package.json中配置 "sideEffects": [ '*.css', '@babel/polyfill' ],  // 避免那種直接import引入的文件被忽略番枚,如 import '@babel/polyfill'
  optimization: {   // tree shaking 支持法严,production 模式中只不需要配置這塊损敷,只需要package.json中配置 "sideEffects"
    usedExports: true
  },
  output: {
    publicPath: '/',        // 指向跟目錄。對應(yīng)server.js   一個node服務(wù) 
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [
          'style-loader',
          { 
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              // modules: true
            } 
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.(eot|woff2|woff|ttf|svg)$/,
        use: [
          'file-loader',
        ]
      },
      { test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        // options: {   // options里面的內(nèi)容也可以都提出去深啤,提到根目錄下的.babelrc,就不需要在這里寫了
        //   // presets: [["@babel/preset-env", {
        //   //   targets: {
        //   //     chrome: "58",               // 如果chrome 58版本支持拗馒,就不去轉(zhuǎn)換了
        //   //     ie: "11"
        //   //   },
        //   //   useBuiltIns: "usage",
        //   //   "corejs": "3"
        //   // }]]
        //   plugins: [["@babel/plugin-transform-runtime", {  // 在寫類庫的時候可以這么配置,避免全局引入@babel/polyfill從而造成數(shù)據(jù)污染溯街,此方式會以閉包等注入有作用域诱桂,不會污染到其他地兒
        //     "absoluteRuntime": false,
        //     "corejs": 2,
        //     "helpers": true,
        //     "regenerator": true,
        //     "useESModules": false,
        //     "version": "7.0.0-beta.0"
        //   }]]
        // }
      }
    ]
  }
}

development 和 production 模式的區(qū)分打包

可以創(chuàng)建 webpack.dev.js 和 webpack.prod.js 兩個配置文件來區(qū)分開發(fā)和生產(chǎn),通過package.json里面配置命令 --config webpack.dev.js 等等
將一些公用的代碼呈昔,提取到webpack.common.js文件中挥等,避免冗余

npm install webpack-merge -D通過這個插件,將webpack.common.js中的代碼堤尾,分別和dev和prod中的代碼進行合并

const merge = require('webpack-merge)
const commonConfig = require('./webpack.common.js')
...
module.exports = merge(commonConfig, devConfig)

webpack 和 Code Splitting 代碼分割

如果代碼不分割肝劲,打包的體積會很大,加載速度變慢,比如一些插件庫 loadsh 等是不需要被時刻打包的辞槐,手動代碼分割掷漱,可以自entry里面做多個打包入口,這樣就可以打包成多個文件

自動配置的話榄檬,可以在之前的optimization里面做一些配置
代碼分割和webpack沒有任何關(guān)系卜范,只是webpack 可以更優(yōu)雅的實現(xiàn)代碼分割

 optimization: {
  splitChunks: {
      chunks: 'all'
  }
}

異步導(dǎo)入 實驗性語法,需要用babel去解析鹿榜,安裝npm install babel-plugin-dynamic-import-webpack -D
.babelrc 配置

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "chrome": "58",
          "ie": "11"
        },
        "useBuiltIns": "usage",
        "corejs": "3"
      }
    ],
    "@babel/preset-react"
  ],
  plugins: ['dynamic-import-webpack']
}

/* webpackPrefetch: true */ 表示網(wǎng)絡(luò)空閑時開始加載海雪,首屏渲染優(yōu)化點,這樣點擊事件觸發(fā)的時候,不會有體驗問題犬缨,應(yīng)用場景喳魏,首頁有登錄彈窗的時候,可以把怀薛,登錄框采用異步加載刺彩,優(yōu)化首屏渲染速度

function getComponent() {
  return import(/* webpackPrefetch: true */'loadsh').then(({ default: _ }) => {
    var element = document.createElement('div');
    element.innerHTML = _.join([1,2,3], '-')
  })
}
document.addEventListener('click',() => {
  getComponent().then(element => {
    document.body.appendChild(element) 
  })
})

webpack 會對同步和異步載入的代碼,都會進行分割(默認參數(shù)async 只對異步代碼進行分割枝恋,因為webpack更多希望你在代碼書寫的時候就采用異步创倔,優(yōu)雅的代碼)。代碼分割焚碌,和webpack無關(guān)畦攘,webpack實現(xiàn)代碼分割的方式有兩種,1同步代碼的分割十电,只需要在webpack配置文件中配置知押,optimization的配置,2異步代碼鹃骂,無需任何配置台盯,會自動進行代碼的分割

splitChunksPlugin 配置參數(shù)的詳解

使用官方提供的異步導(dǎo)入兼容插件
npm install --save--dev @babel/plugin-syntax-dynamic-import

更改chunk名字,可以使用注釋的寫法 /* webpackChunkName: 'loadsh' */

默認配置

module.exports = {
  /...
// todo 待后續(xù)仔細研究
  optimization: {   // tree shaking 支持畏线,production 模式中只不需要配置這塊静盅,只需要package.json中配置 "sideEffects"
    usedExports: true,
    splitChunks: {
      chunks: 'async',     // 只對異步代碼生效  參數(shù)all 同步異步都會分割
      minSize: 20000,   // 大于 20000 字節(jié)才去做分割
      minRemainingSize: 0,
      maxSize: 9000000,   // 如果被分割的代碼大于9000000 字節(jié),會對這些文件嘗試進行二次分割
      minChunks: 1,   // 代碼被使用多少次之后寝殴,才進行代碼分割
      maxAsyncRequests: 30,   // 同時加載的模塊最多是30個蒿叠,超過30個就不會做代碼分割了
      maxInitialRequests: 30,  // 入口文件,最多可以分割30個蚣常,多了就不在分割 
      automaticNameDelimiter: '~',  // 打包出文件市咽,組合文件名的連接符
      enforceSizeThreshold: 50000,
      cacheGroups: {   // 同步代碼會走這里,如果是node-modules下的會分割在defaultVendors組中, cacheGroups和 chunks配合使用
        defaultVendors: {  // cacheGroups 緩存組抵蚊,打包多個文件的時候施绎,先放在緩存組里面曼验,最后再合并輸出
          test: /[\\/]node_modules[\\/]/,
          priority: -10,    // 多個組之間的匹配規(guī)則,值越大優(yōu)先級越高
          filename: 'vendors.js'
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,    // 如果之前這個模塊被打包過粘姜,那么再次打包的時候就會忽略這個模塊使用之前打包好的
          filename: 'common.js'
        }
      }
    }
  },

Lazy Loading 懶加載鬓照,Chunk是什么

打包分析,Preloading Prefetching

webpack.github.com/analyse
將打包的描述文件放在stats.json中
"build": "webpack --profile --json > stats.json",
將輸出的文件上傳到 webpack.github.com/analyse中會查看打包的一些分析依賴

首屏渲染優(yōu)化

盡量去寫孤紧,異步加載的代碼豺裆,提高代碼的利用率,覆蓋率 commd + shif + p 搜索 show cover 進行瀏覽器覆蓋率調(diào)試
/* webpackPrefetch: true */ 表示網(wǎng)絡(luò)空閑時開始加載号显,首屏渲染優(yōu)化點
webpackPrefetch webpackPreLoad 的區(qū)別 webpackPrefetch會等待頁面加載完之后頁面空閑的時候再加載你的代碼臭猜,webpackPreLoad不會等待會一起加載,所以還是webpackPrefetch比較合適(注意瀏覽器的兼容)

Css文件的代碼分割

Css文件的代碼分割 MiniCssExtractPlugin替換style-loader押蚤, 在使用此插件做css 代碼分割的時候蔑歌,一定要主要的是tree shaking 中要排除 *.css 一面,import引入的css 被忽略

Webpack與瀏覽器緩存(Caching)

contenthash 如果打包出的文件沒有變化此值不變揽碘,有變化才會變

output: {
  filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].js'
}  
// 兼容低版本次屠,防止contenthash不改變內(nèi)容也變
// 低版本中對于各個js之間的依賴關(guān)系(manifest)有可能會變,所以配置如下雳刺,將存在與各個js之間的依賴關(guān)系提取出來劫灶,這樣打包會多生成一個runtime.js的文件
optimization: {   
  runtimeChunk: {
    name: 'runtime'
  }
}

Shimming 墊片

假如你引用的一個npm包依賴另一個文件,而猶豫webpack的模塊化打包掖桦,你在外層引入這個依賴文件是不能被npm包所找到本昏,這時候就需要用到,墊片 webpack 提供的一個插件webpack.ProvidePlugin

// 當(dāng)你的模塊中使用到$這個變量的時候枪汪,這個插件會在編譯的過程中自動幫你引入jquery涌穆,給你自動添加類似  `import $ from 'jquery'` 這樣的代碼
new webpack.ProvidePlugin({
  $: 'jquery',
  _join: ['loadsh', 'join']     // 當(dāng)你使用_join的時候webpack會自動幫你吧 loadsh中的join模塊引入進去賦值給_join
})

一個模塊中的this始終指向模塊自身,其上下文雀久,如果我想讓一個模塊中的this指向window呢該怎么做可以借助插件 npm install imports-loader --save-dev

module: {
  rules: [{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
      {
         loader: 'babel-loader'
      },
      {
        loader: 'imports-loader?this=>window'     //  改變了webpack的一些默認行為
      }
    ]
  }]
}

環(huán)境變量的使用

module.exports = (env) => {
  if(env && env.production) { // 生產(chǎn)
    return merge(commonConfig, prodConfig);
  }else { // 開發(fā)
    return merge(commonConfig, devConfig);
  }
}
"build": "webpack --env.production --config ./build/webpack.common.js"  // 傳遞一個env.production全局變量

Library 的打包

上一篇webpack初識

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宿稀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岸啡,更是在濱河造成了極大的恐慌原叮,老刑警劉巖赫编,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巡蘸,死亡現(xiàn)場離奇詭異,居然都是意外死亡擂送,警方通過查閱死者的電腦和手機悦荒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘹吨,“玉大人搬味,你說我怎么就攤上這事。” “怎么了碰纬?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵萍聊,是天一觀的道長。 經(jīng)常有香客問我悦析,道長寿桨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任强戴,我火速辦了婚禮亭螟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骑歹。我一直安慰自己预烙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布道媚。 她就那樣靜靜地躺著扁掸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪最域。 梳的紋絲不亂的頭發(fā)上也糊,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音羡宙,去河邊找鬼狸剃。 笑死,一個胖子當(dāng)著我的面吹牛狗热,可吹牛的內(nèi)容都是我干的钞馁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼匿刮,長吁一口氣:“原來是場噩夢啊……” “哼僧凰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熟丸,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤训措,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后光羞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绩鸣,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年纱兑,在試婚紗的時候發(fā)現(xiàn)自己被綠了呀闻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡潜慎,死狀恐怖捡多,靈堂內(nèi)的尸體忽然破棺而出蓖康,到底是詐尸還是另有隱情,我是刑警寧澤垒手,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布蒜焊,位于F島的核電站,受9級特大地震影響科贬,放射性物質(zhì)發(fā)生泄漏山涡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一唆迁、第九天 我趴在偏房一處隱蔽的房頂上張望鸭丛。 院中可真熱鬧,春花似錦唐责、人聲如沸鳞溉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熟菲。三九已至,卻和暖如春朴恳,著一層夾襖步出監(jiān)牢的瞬間抄罕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工于颖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呆贿,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓森渐,卻偏偏與公主長得像做入,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子同衣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359