下載啥的就很少說了,就看看咱們項目中常常用到的一些配置。碼字不易,喜歡的話點個💗哦 ~~~css
webpack只是一個打包模塊的機制,只是把依賴的模塊轉化成能夠表明這些包的靜態文件。webpack就是識別你的 入口文件。識別你的模塊依賴,來打包你的代碼。至於你的代碼使用的是commonjs仍是amd或者es6的import。webpack都會對其進行分析。來獲取代碼的依賴。webpack作的就是分析代碼。轉換代碼,編譯代碼,輸出代碼。webpack自己是一個node的模塊,因此webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的) webpack中每一個模塊有一個惟一的id,是從0開始遞增的。整個打包後的bundle.js是一個匿名函數自執行。參數則爲一個數組。數組的每一項都爲個function。function的內容則爲每一個模塊的內容,並按照require的順序排列。html
清空打包文件。
new CleanWebpackPlugin()
, 能夠不傳參,配置參考這裏vue
sourcemap就是一個信息文件,裏面儲存着位置信息。目的是爲了解決開發代碼與實際運行代碼不一致時幫助咱們debug到原始開發代碼的技術。node
cheap-module-eval-source-map
cheap-module-source-map
npm i webpack-dev-server -Dreact
devServer: {
// open: true, // 打開瀏覽器
// port: 8080, // default
hot: true, // HMR,不刷新頁面就能應用你改過的css樣式
hotOnly: true, // 若是HMR沒生效,也不刷新頁面
contentBase: './dist', // 告訴服務器從哪裏提供內容。只有在您但願提供靜態文件時才須要這樣作。
proxy: {
'/api': 'http://localhost:3000' // 若是咱們訪問localhost:8000/api ,則轉發請求到localhost:3000
}
},
複製代碼
+ if (module.hot) {
+ module.hot.accept('./print.js', function() {
+ // 檢測到print.js中的更改時,咱們告訴webpack接受更新後的模塊。
+ console.log('Accepting the updated printMe module!');
+ printMe();
+ })
+ }
複製代碼
經過webpack-dev-middleware 配合 express 能夠本身搭建一個簡單的webpack-dev-server,經過node來運行webpack,代碼以下:webpack
server.js
const webpack = require('webpack')
const middleware = require('webpack-dev-middleware')
const express = require('express')
const config = require('./webpack.config.js')
// webpack 編譯器
const compiler = webpack(config)
const app = express()
app.use(
middleware(compiler, {
// webpack-dev-middleware options
publicPath: config.output.publicPath
})
)
app.listen(3000, () => {
console.log('Example app listening on port 3000!')
})
複製代碼
進入官網,打開 setup, 進入webpack,查看相關文檔git
// npm install --save-dev babel-loader @babel/core
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 不包含
loader: "babel-loader" // webpack 和 babel 作通訊的橋樑
}
]
}
// 還須要配置options 或者 .babelrc 文件
npm install @babel/preset-env --save-dev
{
test: /\.js$/,
exclude: /node_modules/, // 不包含
loader: "babel-loader", // webpack 和 babel 作通訊的橋樑
options: {
"presets": ["@babel/preset-env"]
}
}
// 可是,這只是將ES6 轉 ES5,有一些語法好比promiss,map等,低版本仍是不認識,這就要使用 @babel/polyfill 了
複製代碼
// 提供polyfill是爲了方便,可是您應該將它與@babel/preset-env和useBuiltIns選項一塊兒使用
// 這樣它就不會包含並不是老是須要的整個polyfill。不然,咱們建議您手動導入各個填充。
npm install --save @babel/polyfill
// 而後在代碼的頂部引入:
import "@babel/polyfill";
"presets": [["@babel/preset-env"], {
targets: {
chrome: "67", // chrome 版本大於67的,就不須要將ES6轉ES5了,由於chrome對ES6已經支持的很好了
"ie": "11"
},
useBuildIns: "usage" // useBuiltIns選項,若是設置成"usage",那麼將會自動檢測語法幫你require你代碼中使用到的功能。也不須要額外引入@babel/polyfill 了
}]
複製代碼
經過 .babelrc 來聲明 參考文檔es6
參考文獻github
// npm install --save-dev @babel/plugin-transform-runtime
// npm install --save @babel/runtime-corejs2
.babelrc
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2, // 表明須要使用corejs的版本; npm install --save @babel/runtime-corejs2
"helpers": true,
"regenerator": true,
"useESModules": false // 按需引入babel/polyfill (注入低版本的polyfill)
}
]
]
}
複製代碼
安裝包 npm i @babel/preset-env @babel/preset-react -Dweb
tree shaking 是一個術語,一般用於描述移除 JavaScript 上下文中的未引用代碼(dead-code)。它依賴於 ES2015 模塊語法的 靜態結構 特性,例如 import 和 export。不支持 require 的引入方式。這是由於 ES 模塊的引入方式是靜態的,而require 的引入方式是動態的。
mode: "development",
optimization: {
usedExports: true // 查看那些模塊被使用了,使用了的就打包
}
// 還要再 package.json 中配置一個屬性
"sideEffects": false
// "side effect(反作用)" 的定義是,在導入時會執行特殊行爲的代碼,而不是僅僅暴露一個 export 或多個 export。舉例說明,例如 polyfill,它影響全局做用域,而且一般不提供 export。
"sideEffects": ["@babel/polyfill", "*.css"] // 不對 @babel/polyfill和全部引入的css文件 做tree shaking
複製代碼
注意: 在開發環境下,使用tree shaking 其實仍是會將沒有使用的模塊打包進 bundle.js 中,只不過會提醒你那些模塊沒有使用。若是使用 mode: production; 那咱們就不須要使用 optimization 配置項了,而且將設置 devtool: cheap-module-source-map。可是 sideEffects 仍是要使用。
npm i webpack-merge -D 用來合併 webpack 模塊
看看 webpack.prod.js 的用法
webpack.prod.js
const merge = require('webpack-merge')
const devConfig = require('webpack.common.js')
cosnt prodConfig = {
mode: 'production',
devtool: 'cheap-module-source-map'
}
module.exports = merge(prodConfig, merge)
複製代碼
而後修改 package.json 中的配置
// 啓動dev
"dev": "webpack-dev-server --config ./build/webpack.dev.js"
// 啓動prod
"build": "webpack --config ./build/webpack.prod.js"
複製代碼
注意: 若是咱們的webpack配置放在build文件夾中,而且咱們的配置中使用 clean-webpack-plugin,那麼它的配置也須要發生改變
new CleanWebpackPlugin(['dist']) // 指的是刪除當前目錄下的 dist 目錄,可是咱們的 dist 目錄須要放在 build 文件夾同級目錄下。
// 能夠這樣作 : 在 github 上搜索 clean-webpack-plugin
new CleanWebpack(['dist'], {
root: path.resolve(__dirname, '../') // 指定根路徑
})
複製代碼
代碼分離能夠用於獲取更小的 bundle,以及控制資源加載優先級,若是使用合理,會極大影響加載時間。
經常使用的代碼分離方法有三種:
入口起點:使用 entry 配置手動地分離代碼。(優勢:最簡單最直觀。缺點:對咱們的示例來講毫無疑問是個嚴重問題,由於咱們在 ./src/index.js 中也引入過 lodash,這樣就形成在兩個 bundle 中重複引用。)
防止重複:使用 SplitChunksPlugin 去重和分離 chunk。
optimization: { // 這裏能夠配置 code splitting, 還能夠配置 tree shaking 時須要的 usedExports
splitChunks: {
chunks: 'all'
}
}
複製代碼
將index.js 和 another_module.js 中的 lodash 庫抽離出來了。
經過模塊中的內聯函數調用來分離代碼。 咱們再也不使用 statically import(靜態導入) lodash,而是經過 dynamic import(動態導入) 來分離出一個 chunk。 查看官網demo
想要消除 開發環境 和 生產環境 之間的 webpack.config.js 差別,你可能須要環境變量(environment variable)。***參考文檔***
const path = require('path');
module.exports = env => {
// Use env.<YOUR VARIABLE> here:
console.log('NODE_ENV: ', env.NODE_ENV); // 'local'
console.log('Production: ', env.production); // true
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
};
複製代碼
// 在上面的 環境區分中,咱們在 webpack.common.js 中引入 devConfig、prodConfig、webpack-merge,而後經過 env 來判斷當前的打包命令是開發環境仍是生產環境。
module.exports = env => {
if (env && env.production) {
return merge(commonConfig, prodConfig);
} else {
return merge(commonConfig, devConfig);
}
}
複製代碼
除了打包應用程序,webpack 還能夠用於打包 JavaScript library. 參考文檔
// webpack.config.js
externals: ['lodash']
// 不打包lodash,可是別人引入咱們的庫的時候,就必須引入lodash.js,由於咱們的庫依賴lodash
複製代碼
咱們經過搭建一個簡易 server 下,測試下這種離線體驗。這裏使用 http-server package:npm install http-server --save-dev 參考
{
"scripts": {
+ "build": "webpack",
+ "start": "http-server dist"
}
}
複製代碼
npm install workbox-webpack-plugin --save-dev
const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
+ title: '漸進式網絡應用程序'
+ }),
+ new WorkboxPlugin.GenerateSW({
+ // 這些選項幫助快速啓用 ServiceWorkers
+ // 不容許遺留任何「舊的」 ServiceWorkers
+ clientsClaim: true,
+ skipWaiting: true
+ })
]
複製代碼
接下來咱們註冊 Service Worker,使其出場並開始表演。經過添加如下注冊代碼來完成此操做:
import _ from 'lodash';
import printMe from './print.js';
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/service-worker.js').then(registration => {
+ console.log('SW registered: ', registration);
+ }).catch(registrationError => {
+ console.log('SW registration failed: ', registrationError);
+ });
+ });
+ }
複製代碼
TypeScript 是 JavaScript 的超集,爲其增長了類型系統,能夠編譯爲普通 JavaScript 代碼 參考
npm install --save-dev typescript ts-loader
複製代碼
{
"compilerOptions": {
"outDir": "./dist/", // 當前目錄下的dist
"noImplicitAny": true, //
"module": "es6", // 使用ES Module 引入方式
"target": "es5", // 轉換成 ES5 的代碼
"jsx": "react",
"allowJs": true // 容許引入 js 文件
}
}
複製代碼
npm i @types/lodash -D // 經過 @types/ + 庫的名字便可
複製代碼
// 請求到 /api/users 如今會被代理到請求 http://localhost:3000/api/users
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'header.json': 'demo.json' //咱們請求header.json的時候,轉而去請求demo.json
},
secure: false, // 默認狀況下,不接受運行在 HTTPS 上,且使用了無效證書的後端服務器。若是你想要接受,修改配置
bypass: function(req, res, proxyOptions) { // 攔截,若是請求的是 html 類型的數據,就直接返回 html文件,不進行轉發
if (req.headers.accept.indexOf("html") !== -1) {
console.log("Skipping proxy for browser request.");
return "/index.html";
}
}
}
}
}
複製代碼
當使用 HTML5 History API 時,任意的 404 響應均可能須要被替代爲 index.html.在作單頁面路由的時候,須要配置此選項。
devServer: {
historyApiFallback: true
}
複製代碼
約束代碼規範的工具,團隊開發尤爲重要
// 安裝
npm i eslint -D
// 初始化配置文件
npx eslint --init
複製代碼
注意: 在webpack.devServer中配置一個屬性:npm i eslint-loader -D (會下降打包的速度哦 ! 能夠在options中配置 cache 屬性) 參考官網配置
devServer: {
overlay: true, // 將報錯顯示在頁面上
}
複製代碼
import index from './src'
,其實就是引入的src下的index.js,咱們也能夠配置其它的文件,好比 main.js,hello.js ...DLLPlugin 和 DLLReferencePlugin 用某種方法實現了拆分 bundles,同時還大大提高了構建的速度。
將咱們打包好的第三方庫源代碼引入到 HtmlWebpackPlugin 生成的 index.html 中
這個插件是在 webpack 主配置文件(webpack.common.js)中設置的。它會結合manifest.json、第三方庫源碼文件以及咱們引入的第三方庫文件作一個分析,若是它發現咱們引入的文件在源碼文件裏面已經有了,它就直接拿過來用了,而不會node_modules裏面找了。
new webpack.DllRefrencePlugin({
manifest: path.resolve(__dirname, './vendors.manifest.json')
})
複製代碼
webpack 默認是使用 node.js 來運行,即採用的單線程機制打包過程
描述越詳細,就越慢哦 ~
結合分析,查看哪一個模塊打包耗時比較長,作針對性處理
採用 webpack-dev-server
好比壓縮 css 或者 js 的插件
本質就是建立多個entry 以及 HtmlWebpackPlugin 來實現的
其實 loader 就是一些函數(不能使用箭頭函數哦,由於this),接受的參數就咱們的源代碼,經過函數來作一些處理並返回而已。loaderAPI
// myself-loader.js
module.exports = function (source) {
return something ...
}
// 而後在 module 中配置
module: {
rules: [
{
test: /\.js$/,
loader: path.resolve(__dirname, './loaders/myself-loader.js')
}
]
}
// 如何傳參?
// 配置 options 以後,在咱們寫的 laoder 裏面經過 this.query 就能獲取到啦 !!!
{
test: /\.js$/,
use: [
loader: path.resolve(__dirname, './loaders/myself-loader.js'),
options: {
name: 'alex.cheng'
}
]
}
// 本身定義的 loader 如何像引入 node_modules 裏面 loader 同樣引入呢 ?
resolveLoader: {
modules: ["node_modules", "./loader"] // 若是在node_modules裏沒找到,就到當前文件loader中去找
}
複製代碼
咱們使用別人的 plugin 的時候都是怎麼使用的呢? 是否是都要 new Plugin ? 因此啊,咱們的 plugin 都是經過構造函數來編寫的。來看一個簡單的例子
// plugin: alex-cheng-webpack-plugin.js
class AlexChengWebpackPlugin {
constructor() {
console.log('alex.cheng plugin is excuted !')
}
}
module.exports = AlexChengWebpackPlugin
// webpack.config.js
const path = require('path')
const AlexChengWebpackPlugin = require('./src/myPlugins/alex-cheng-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, './dist/')
},
plugins: [
new AlexChengWebpackPlugin() // 調用就好了啦 !!
]
}
複製代碼
看!咱們本身的插件就執行了咯 ! 詳情能夠參考 官網API