[深刻16] webpack

導航

[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hookscss

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIhtml

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程前端

前置知識

一些單詞

proposal:提議,建議
decorator:修飾
expose:暴露
license:執照,許可
compress:壓縮
determine:肯定,查明
wildcard:通配符
collapse:塌方,瓦解
quote:引用

extract:抽離,提取
optimization:最佳優化
plug:插頭,插座  (pluggable:可插拔)
complete:完成

represents:表明
複製代碼

package.json

name
- 定義包的名稱,是發佈到npm註冊表時,軟件包顯示的名稱
- 必須是小寫,容許( 連字符和下劃線 ),不容許 ( 空格和其餘字符 )
- name: 'webpack-learning'
- 注意:若是要發佈到npm上,包的名稱必須是惟一的

main !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- 定義項目的入口點
- 默認是index.js

version
- 軟件的當前版本

license
- 代碼許可證
- MIT
- ISC

author 和 contributors
- author:一我的
- contributors:多我的
- "author": "...",
- "contributors": [{
	"name": "...",
	"email": "...",
	"url": "..."
}],

keywords
- 搜索索引
- "keywords": ["server", "osiolabs", "express", "compression"]
複製代碼

path.resolve 和 path.join 和 __dirname變量

  • path.resolve() --------------------------- 生成絕對路徑
    • path.resolve([...paths])
    • 將( 路徑或路徑片斷 ) 的序列解析爲 ( 絕對路徑 )
    • 參數:路徑或路徑片斷的序列
    • 返回值:一個絕對路徑
    • 注意:
      • 不傳參數將返回當前工做目錄的絕對路徑
      • 0長度的path片斷將被忽略
  • path.join()--------------------------------- 拼接路徑
    • path.join([...paths])
    • 注意:
      • 0長度的路徑將被忽略
  • __dirname -------------------------------- 當前模塊的目錄名,是一個絕對路徑

process.env ---------------------------- 返回包含用戶環境的對象

  • process.env 返回包含用戶環境的對象
  • 注意:process.env對象上分配的屬性,值會被隱式轉換爲字符串,當值不是string,number,boolean時可能會報錯
  • 注意:能夠修改此對象,但這些修改不會反映到 Node.js 進程以外
  • 刪除屬性:delete
    • delete process.env.xxxx
  • 注意:在windows系統上,環境變量不區分大小寫
  • process.env.NODE_ENV不是process.env對象的原有屬性,是自定義的
    • 緣由:在window和mac有差別
    • 因此:通常經過 ( cross-env ) 插件來設置
    • npm install --save-dev cross-env
    • cross-env
(1) 生效
process.env.foo = 'bar';
console.log(process.env.foo);


(2) process.env的屬性會隱式轉換爲字符串
process.env.test = null;
console.log(process.env.test);
// => 'null'
process.env.test = undefined;
console.log(process.env.test);
// => 'undefined'


(3) delete 刪除屬性
process.env.TEST = 1;
delete process.env.TEST;
console.log(process.env.TEST);
// => undefined


(4) window上 ( 環境變量 ) 不區分大小寫
process.env.TEST = 1;
console.log(process.env.test);
// => 1
複製代碼
---
cross-env

{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
  }
}
複製代碼

會用到的依賴

- webpack webpack-cli
- html-webpack-plugin
- webpack-dev-server

css相關
- style-loader
- css-loader
- less-loader
- 抽離css
- mini-css-extract-plugin
- 前綴
- postcss-loader
- autoprefixer
- 壓縮css
- (注意:用於生產環境纔去壓縮css,由於影響打包速度)
- optimize-css-assets-webpack-plugin
- uglifyjs-webpack-plugin

js相關
- babel-loader
- @babel/core
- @babel/preset-env
- @babel/plugin-proposal-decorators
- @babel/plugin-proposal-class-properties
- @babel/plugin-transform-runtime
- @babel/runtime
- @babel/polyfill
- eslint ellint-loader babel-eslint
- expose-loader

圖片相關
- file-loader
- url-loader
- html-withimg-loader
複製代碼

代碼分割 webpack.DllPlugin 和 optimization.splitChunks 的區別

  • (webpack.DllPlugin,webpack.DllReferencePlugin) 和 optimization.splitChunks 都能實現代碼分割
  • 優先使用 optimization.splitChunks.cacheGoup
  • SplitChunksPlugin
SplitChunksPlugin


默認配置:
module.exports = {
  //...
  optimization: { 
  // -------------- 默認配置,該段配置的效果和 optimization: {splitChunks: {}} 效果同樣
  // -------------- 即 splitChunks 配一個空對象和下面的代碼等價
    splitChunks: {
      chunks: 'async', 
      // -------------- chunks: 'async' 只對異步引入的代碼就行代碼分割,好比 import().then()語法動態引入的包或者路由組件
      // -------------- chunks: 'all' 對同步引入和異步引入的代碼都作代碼分割,還需後續配置cacheGroups
      // -------------- chunks: 'initial' 對同步代碼進行分割
      minSize: 30000,
      // -------------- minSize: 30 000 表示當包大於( 30KB )時,就進行代碼分割
      maxSize: 0,
      // -------------- maxSize表示對打包後的庫進行二次拆分,可配可不配
      minChunks: 1,
      // -------------- minChunks 表示當一個模塊至少使用多少次時才進行代碼分割
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~', // 文件命名之間的鏈接符,好比name和入口文件名之間的鏈接
      name: true,
      cacheGroups: { // 一個可能包含多個包的組
        vendors: { 
        // ----------------- 表示若是知足chunks設置項,而且在node_modules文件夾下,就打包成 vendors~main.js
        // ----------------- vendors~main.js 中的 main 表示入口文件打包後的文件名是 main.js
        // ----------------- 若是要修改知足chunks的配置引入的包打包的名字用:filename 去配置
        // ----------------- cacheGroups 和 chunks 是配合來用的
          test: /[\\/]node_modules[\\/]/, 
          //---------------- 從node_modules文件夾中引入的,實際上就是第三方組件庫,依賴庫
          priority: -10,
          // --------------- priority表示當同時知足vendors和default時, piroity越大優先級越高,(優先級)
          filename: 'vendors', // ------------- 打包後知足chunks和以上各個條件的引入的包會被打包到名爲 vendors.js 的文件中
        },
        default: { // 不知足vendors可是又知足chunks等以上的一些條件,說明須要作代碼分割,但不是vendors,則會走default
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true, 
          // reuseExistingChunk表示一個包若是打包過了,在別的包中又引用了,打包別的包時就再也不打包了,直接引用
          filename: 'common'
        }
      }
    }
  }
};
複製代碼

webpack基礎

  • webpack是一個 ( 模塊打包 ) 工具,最基本的就是一個js模塊打包工具,若是添加了loader則能夠打包任何相應的資源
  • 能夠 0 配置,可是爲了符合項目需求,通常都會本身配置
0配置下

默認配置運行:-------------------------------------- npx webpack xxx.js
打包後生成的 ( 文件夾 ) 名稱:---------------------- dist
打包後生成的 ( 文件 ) 名稱:------------------------ main.js

(2)
webpack默認配置文件
默認的webpack配置文件名稱:------------------------- webpack.config.js || webpackfile.js

(3)
如何用命令行指定webpack的配置文件,並執行這個文件
---------------------------------------------------- npx webpack --config xxxxx
複製代碼

webpack webpack-cli

  • npm install -D webpack-cli webpack
  • 注意是開發時依賴,線上使用打包後的文件

webpack-dev-server

  • npm install -D webpack-dev-server
  • webpack-dev-server能夠啓一個簡單的web服務,而且具備 live-reloading 實時從新加載
  • 配置項:
    • contentBase:啓動的服務從哪一個目錄中加載資源
    • port:服務端口號
    • publicPath:設置公共路徑,此路徑下的打包文件,能夠在瀏覽器中訪問
      • 好比部署到CDN上,html的全部引入資源都須要加上CDN的地址做爲公共路徑,這時須要設置publciPath
    • proxy:設置代理 ,使用的是 http-proxy-middleware
    • compress:是否開啓 gzip 壓縮
    • hot:是否開啓熱更新 ( 模塊熱替換 )
    • open:是否在啓動服務後打開瀏覽器
    • host:指定host
    • color:控制檯彩色輸出,boolean,注意只用於命令行
  • proxy
proxy

var apiProxy = proxy('/api', { target: 'http://www.example.org' });
//                   \____/   \_____________________________/
//                     |                    |
//                   context             options

(1)
proxy: {
    '/api': 'http://www.baidu.com'
}
請求:'/api/user'
會被代理到:'http://www.baidu.com/api/user'

(2)
需求:如何在真實的請求路徑中去掉 '/api'
解決:重寫路徑
proxy: {
    '/api': {
        target: 'http://www.baidu.com',
        pathRewrite: {
            '^/api': ''   //------------------------------------- remove path 即刪除 '/api'
        }
    }
}
請求:'/api/user'
會被代理到:'http://www.baidu.com/user'
複製代碼
---
webpack-dev-server

devServer: { // --------------------------------------------- webpack-dev-server配置項
  contentBase: path.join(__dirname, 'dist'), // ------------- 啓動服務加載的靜態資源
  port: '5000', // ------------------------------------------ 端口號
  compress: true, // ---------------------------------------- 是否開啓 gzip 壓縮
  open: true, // -------------------------------------------- 是否打開瀏覽器標籤,在服務啓動後
  proxy: { // ----------------------------------------------- 代理
    '/api': { // -------------------------------------------- context,須要代理的路徑
      target: 'http://yapi.wxb.com.cn', // ------------------ 目標地址
      pathRewrite: { // ------------------------------------- 路徑重寫
        '^/api': '' // -------------------------------------- remove path 至關於把 '/api''' 代替
      }
    }
  }
},
複製代碼

proxy 實例

(1)
index.js
const api = new XMLHttpRequest()
api.open('GET', '/api/name', true) // --------------------------------- 請求路徑 ( '/api/name' )
api.onload = function() {
  const res = api.response
  console.log(res, 'res')
}
api.send()


(2)
devServer: {
  contentBase: path.join(__dirname, 'dist'),
  port: '5000',
  compress: true,
  open: true,
  proxy: {
    '/api': {
      target: 'http://localhost:7000', // ----------------------- 服務代理到http://localhost:7000
      pathRewrite: {
        '^/api': '' 
        // ------------------------------------------------------ 重寫'/api'
        // ------------------------------------------------------ '/api/name' 將代理到 'http://localhost:7000/name'
      }
    }
  }
},


(3)
server => index.js
var express = require('express')
var app = express()
app.get('/name', (req, res) => { // ----------------------------- 服務端啓動在:'http://localhost:7000/name'
  res.json({
    name: 'woow_wu7'
  })
})
app.listen(7000, () => console.log('the server is running on port 7000'))
複製代碼

html-webpack-plugin

  • npm install html-webpack-plugin -D
  • 配置項:
    • template:模板html文件
    • filename:打包後的文件名
    • hash:在html引用js時是否使用hash戳
    • minify:用於 ( 生產環境 ) 即mode=production中,是一個 ( 布爾值 ) 或者 ( 對象 )
      • removeAttributeQuotes:去除html屬性值的雙引號
      • collapseWhitespace:摺疊空行,即只有一行代碼
html-webpack-plugin

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

plugins: [ // ------------------------------------------------- plugins是一個插件數組
  new HtmlWebpackPlugin({
    template: './src/index.html', // -------------------------- 模板html
    filename: 'index.html', // -------------------------------- 打包後的html的文件名
    hash: true, // -------------------------------------------- html引用的js是否帶有hash戳
    minify: { // ---------------------------------------------- 壓縮優化
      removeAttributeQuotes: true, // ------------------------- 刪除html標籤屬性值的雙引號
      collapseWhitespace: true, // ---------------------------- 摺疊空行爲一行
    }
  })
]
複製代碼

loader

  • loader加載順序: 從右往左,從下往上
  • loader文檔

css相關

( css-loader ) ( style-loader ) ( less-loader )

  • npm install style-loader css-loader less less-loader -D
  • css-loader
    • 主要用來支持 @import 語法的css引入
  • style-loader
    • 主要做用是把css以 ( style標籤的 ) 形式嵌入到html的head標籤中
    • 注意順序是:先css-loader處理完css後,再style-loader插入到head標籤中
    • 配置項 options
      • insert: 'body' 將style標籤插入到body標籤中,默認是插入到head標籤中
style-loader
css-loader
less-loader 還得安裝 less
sass-loader 還得安裝 node-sass


module: { // 模塊
  rules: [ // 規則
    {
      test: /\.css$/,  // ------------------------ 處理css
      // ----------------------------------------- 正則匹配,注意loader的順序是從右往左,從下往上
      use: [
        {
          loader: 'style-loader',
          options: {
            // insert: 'body' // ----------------- style標籤默認插入到header標籤中,這裏能夠插入到body標籤中
          }
        },
        {
          loader: 'css-loader',
        },
      ]
    },{
      test: /\.less$/, // ------------------------ 處理less
      use: [
        {loader: 'style-loader'},
        {loader: 'css-loader'},
        {loader: 'less-loader'}
      ]
    }
  ]
},
複製代碼

mini-css-extract-plugin ------ 抽離css

  • npm install mini-css-extract-plugin -D
  • 默認狀況下:style-loader是把css文件轉換成 style 標籤內嵌在html中,咱們但願的是用 link 標籤外部引入css,因此用mini-css-extract-plugin來抽離css,經過link標籤把css引入html
  • extract:抽離的意思
  • mini-css-extract-plugin官網
const MiniCssExtractPlugin =  require('mini-css-extract-plugin') // ---------------- 引入

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader, // -------------------------------------------- loader
        {
          loader: 'css-loader',
        },
      ]
    },{
      test: /\.less$/,
      use: [
        MiniCssExtractPlugin.loader,
        {loader: 'css-loader'},
        {loader: 'less-loader'}
      ]
    }
  ]
},
plugins: [
  new MiniCssExtractPlugin({ // ---------------------------------------------------- plugin
    filename: 'main.css' // -------------------------------------------------------- 抽離出來的css文件名
    // filename: 'css/main.css' // ----------------- 這樣會在打包後生成css文件夾,裏面是全部css都在main.css文件中
  })
]
複製代碼

postcss-loader 和 autoprefixer ------ 生成瀏覽器前綴

  • postcss-loader
    • 用來解決瀏覽器前綴,兼容性處理,須要單獨的配置文件 postcss.config.js
    • 解決瀏覽器前綴:(1) 單獨配置postcss.config.js (2) 還需配合插件 autoprefixer
    • 注意loader間的順序:好比須要處理完less => 加上前綴後 => 識別@import => 再抽離css
    • 即先 less-loader => postcss-loader => css-loader => MiniCssExtractPlugin.loader
  • autoprefixer
    • autoprefixer須要給出瀏覽器的一些信息,因此要在 package.json 中添加 browserslist 配置
postcss-loader 和 autoprefixer 解決瀏覽器前綴問題

(1) loader間的順序
(2) postcss-loader須要單獨配置postcss.config.js
(3) autoprefixer須要給出瀏覽器的一些信息,須要在 package.json 中配置 browserslist 屬性

---
package.json中
"browserslist": [
  "defaults",
  "not ie < 9",
  "last 3 version",
  ">1%",
  "ios 7",
  "last 3 iOS versions"
]

---
postcss.config.js中
const autoprefixer = require('autoprefixer')
module.exports = {
  plugins: [autoprefixer]
}

---
webpack.config.js中
rules: [
  {
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      { loader: 'css-loader'},
      { loader: 'postcss-loader'}, //----------------- postcss-loader,注意順序
    ]
  },{
    test: /\.less$/,
    use: [
      MiniCssExtractPlugin.loader,
      {loader: 'css-loader'},
      { loader: 'postcss-loader'},
      {loader: 'less-loader'}
    ]
  }
]
複製代碼

optimize-css-assets-webpack-plugin 和 uglifyjs-webpack-plugin ------ 壓縮css 和 js

  • npm install optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
  • 注意壓縮css是用於 ( 生產環境 ) 纔去壓縮css,由於會影響打包速度
  • 注意:( html-webpack-plugin ) 通 過minify => removeAttribureQuote, collapseWhitespace 來壓縮html也是用在 ( 生產環境 )
  • optimize-css-assets-webpack-plugin
    • 注意:
      • 問題:使用 optimize-css-assets-webpack-plugin 插件後,單獨抽離的css會被壓縮,可是打包的js文件又沒有被壓縮了
      • 解決:使用 uglifyjs-webpack-plugin 插件來解決js沒有被壓縮的問題
  • uglifyjs-webpack-plugin
optimization: { // -------------------------------- 優化項 (optimization:是最佳優化的意思)
  // ---------------------------------------------- 注意:壓縮css和js要在mode=production中才能看到效果,和 html的優化同樣
  minimizer: [
    new OptimizeCssAssetsWebpackPlugin(), // ------ optimize-css-assets-webpack-plugin 壓縮單獨抽離的css
    new UglifyjsWebpackPlugin({ // ---------------- 壓縮js
      cache: true,
      parallel: true, // 平行,並行的意思
      sourceMap: true, // 調試映射
    })
  ]
}
複製代碼

js相關

  • babel-loader
  • @babel/core
  • @babel/preset-env
  • @babel/plugin-proposal-decorators // -------------------- 裝飾器語法
  • @babel/plugin-proposal-class-properties // -------------- class語法
  • @babel/plugin-transform-runtime
  • @babel/runtime // ---------------------------注意是該依賴是 dependencies 而不是 devDependencies
  • @babel/polyfill
  • npm install babel-loader @babel/core @babel/preset-env -D
  • npm install @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
  • npm install @babel/plugin-transform-runtime -D
  • npm install @babel/runtime -S 注意是-S
  • npm install @babel/polyfill -S 注意是-S
  • babel官網
  • 注意點:
    • babel的配置:
      • (1)能夠新建.babelrc文件單獨配置
      • (2)能夠在webpack的loader配置的babel-loader的options中配置
    • @babel/plugin-proposal-decorators 和 @babel/plugin-proposal-class-properties的順序
      • @babel/plugin-proposal-decorators --------------在前
      • @babel/plugin-proposal-class-properties---------在後
.babelrc文件
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false
    }]
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", {"legacy": true}],
    ["@babel/plugin-proposal-class-properties", {"loose": true}],
    ["@babel/plugin-transform-runtime"]
  ]
}


----
@babel/polyfill 插件是直接引入到入口js文件中
require('@babel/polyfill')
複製代碼

加入eslint校驗 (eslint) (eslint-loader) (babel-eslint)

  • npm install eslint eslint-loader babel-eslint -D
  • eslint
  • eslint-loader
  • babel-eslint
  • 注意點:
    • (1) 須要單獨配置 ( .eslintrc.json ) 文件,或者直接在 ( package.json ) 文件的 ( eslintConfig ) 字段中配置rule規則
    • (2) 注意eslint-loader和babel-loader的順序
      • 順序:先校驗再處理
      • 是要先校驗eslint-loader,而後在babel-loader轉換
      • 在eslint-loader的options選中,可使用 enforce: 'pre' 來優先執行,即便順序是eslint-loader在上面或前面
    • (3) 須要安裝bebel-eslint插件 Cannot find module 'babel-eslint'
.eslintrc
{
  "parser": "babel-eslint", // 這裏必須設置
  "parserOptions": {
    "sourceType": "module",
    "allowImportExportEverywhere": true
  },
  "rules": {
    "indent": "off",
    "no-console": 2 // 不容許console.log()
  },
  "env": {}
}


------------
webpack.config.js
{
  test: /\.js$/,
  use: [ // ----------------------------------------------- use能夠是數組 或者 對象
    {
      loader: 'babel-loader',
      options: { // ---------------------------------- 除了在這裏配置外,還能夠單獨設置 .babelrc 文件配置babel
        presets: [ 
          ['@babel/preset-env']
        ],
        plugins: [
          ['@babel/plugin-proposal-decorators', {'legacy': true}],
          ['@babel/plugin-proposal-class-properties', {'loose': true}],
          ['@babel/plugin-transform-runtime']
        ]
      }
    },
    {
      loader: 'eslint-loader', // -------------------------- eslint-loader 
      options: {
        enforce: 'pre'
      }
    }
  ],
  include: path.resolve(__dirname, 'src'),
  exclude: path.resolve(__dirname, 'node_modules'),
},

     // {
      //   test: /\.js$/,
      //   use: { // --------------------------------------- use是對象
      //     loader: 'eslint-loader',
      //     options: {
      //       enforce: 'pre'
      //     },
      //   },
      //   include: path.resolve(__dirname, 'src'),
      //   exclude: path.resolve(__dirname, 'node_modules'),
      // },
複製代碼

loader的類型

  • pre ----------------------- 前置loader
  • post ---------------------- 後置loader
  • normal ------------------- 普通loader
  • inline --------------------- 內聯loader,便可以直接寫在js代碼中的loader

expose-loader 和 wepback.ProvidePlugin()

  • npm install expose-loader -D
  • expose-loader暴露全局的loader
  • expose:暴露
  • expose-loader的三種用法
expose-loader的三種用法

(1)
直接在js中引入:----------------  import $ from 'expose-loader?$!jquery'
上面的操做能夠把:$掛載到window上,便可以經過 ( window.$ ) 訪問到jquery

(2)
在webpack.config.js中 module => rules 中配置
{
  test: require.resolve('jquery'), 
  // require.resolve()是nodejs中的函數,只要require了jquery就進行匹配
  // require.resolve():使用內部的 require() 機制查詢模塊的位置,此操做只返回解析後的文件名,不會加載該模塊。
  use: [
    {
      loader: 'expose-loader',
      options: 'jquery' // 暴露成window.jquery
    },
    {
      loader: 'expose-loader',
      options: '$'  // 暴露成window.$
    }
  ]
},

(3)
在每一個模塊中注入$,不須要在每一個模塊中再引入,可使用 webpack.ProvidePlugin插件,再plugins中加入
new webpack.ProvidePlugin({ // ----------------- webpack.ProvidePlugin()
    $: 'jquery' // ----------------------------- 在每一個模塊中都注入$符
}) 
使用:直接在模塊中就可使用 $ , 並不須要在頭部引入
複製代碼

圖片處理

  • npm install file-loader url-loader html-withimg-loader -D
  • file-loader
    • 會生成一張圖片到build的目錄下,並將圖片的名稱返回回來
  • url-loader
    • 能夠設置大小限制,小於時,url-loader會將圖片轉成base64,大於時,使用file-loader加載圖片
    • 在options: {limit: 200 * 1024}來設置,200k,經過 ( limit ) 來設置
    • options: {outputPath: 'img/'} ----- 將圖片打包到img文件夾下
    • 注意:(1)css全部文件單獨抽離成一個文件放到一個文件夾中是使用 mini-css-extract-plugin (2) 圖片單獨抽離到文件夾中是url-loader中配置options的outputPath
  • html-withimg-loader
    • 注意:使用該插件時可能報錯,須要在url-loader的options中設置 esModule: false
  • 圖片的引入方式
    • 在js中引入,能夠經過new Image().src等
    • 在css中,經過 background-iamge來引入
    • 在html中,經過 img 標籤來引入
file-loader
url-loader
html-withimg-loader

webpack.config.js
{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: [
    {
      // loader: 'file-loader',
      loader: 'url-loader',
      options: {
        limit: 200 * 1024, // ----------------- 小於200k,使用base64編碼圖片,大於使用file-loader加載圖片
        esModule: false, // ------------------- 用於html-withimg-plugin生效
        outputPath: 'img/', // ---------------- 輸出到 img 文件夾中
        publicPath: 'www.baidu.com/' // ------- 單獨配置img的公共路徑,而不是在output中所有配置
      }
    }
  ]
},
{
  test: /\.html$/, // ----------------- html-withimg-loader
  use: 'html-withimg-loader'
}

使用:
js中
import one from './images/1.jpg'
const imgx = new Image(100, 100)
imgx.src = one
if (imgx.complete) {
  addChild()
} else {
  imgx.onload = addChild
}
function addChild() {
  document.body.appendChild(imgx)
}
css中
#div-image {
  width: 200px;
  height: 200px;
  background: url('./images/1.jpg');
}
html中
<img src="./images/1.jpg" alt="" width="300" height="300">
複製代碼

靜態資源分類

  • 圖片
    • url-loader => options => outputPath: 'img/'
  • css
    • mini-css-extract-plugin => filename: 'css/main.css'
  • 公共路徑
    • 在 output => publicPath 中設置公共路徑,當打包後html加載資源時,都會加上公共路徑
    • 好比:不是放在本地,放在CDN上,就要在全部路徑上服務器的地址
  • 若是隻是想給圖片加載公共路徑,而其餘資源不加的話,就要在url-loader中單獨設置,同時output的publicPath不用設置
靜態資源分類

圖片
{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: [
    {
      // loader: 'file-loader',
      loader: 'url-loader',
      options: {
        limit: 200 * 1024,
        esModule: false, 
        outputPath: 'img/' // ----------------------------- 全部圖片將放到打包後的 ( img文件夾中 )
      }
    }
  ]
},


css
plugins: [
  new MiniCssExtractPlugin({
    filename: 'css/main.css' // ---------------------------- 全部css將放入到打包後的 ( css文件夾的main.css中 )
  }),
],


公共路徑:全部路徑都加
output: {
  filename: 'index.[hash:8].js',
  path: path.resolve(__dirname, 'dist'),
  publicPath: 'www.baidu.com/' // -------------------------- 設置公共路徑,打包後html中引入的資源都會加上這個前綴路徑
},

公共路徑:只是圖片加
{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: [
    {
      // loader: 'file-loader',
      loader: 'url-loader',
      options: {
        limit: 200 * 1024,
        esModule: false, 
        outputPath: 'img/',
        publicPath: 'www.baidu.com/' // ---------------------- 只給圖片設置publicPath
      }
    }
  ]
},
複製代碼

打包多頁應用

  • entry:能夠是一個對象
  • output:'[name].js' => []是佔位符 => [name]的值就是entry對象中的key
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: {
    home: './src/home.js',
    other: './src/other.js',
  },
  output: {
    filename: '[name].js', // ---------------------------- 佔位符,name表示entry對象中的 key
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    port: 5000,
    open: true,
    compress: true,
  },
  plugins: [
    new HtmlWebpackPlugin({ // ---------------------------- html-webpack-plugin能夠new多個
      template: './src/index.html',
      filename: 'home.html',
      chunks: ['home'] // --------------------------------- 每一個chunk對應加載哪些打包後的 js 文件
    }),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'other.html',
      chunks: ['other']
    }),
  ]
}
複製代碼

source-map 源碼映射

  • devtool: 'source-map' // 顯示行數,產生map文件
  • devtool: 'eval-source-map' // 顯示行數,不產生map文件

watch 實時打包

watch: true,
watchOptions: {
  aggregateTimeout: 300, // 防抖
  poll: 1000, // 每秒詢問1000次
  ignored: /node_modules/
},

aggregate: 總計,合計的意思
poll:是輪詢的意思
複製代碼

clean-webpack-plugin ---------- 刪除打包後的文件夾,默認是刪除 output.paht 指定的文件夾

const {CleanWebpackPlugin} = require('clean-webpack-plugin')

plugins: [
  new CleanWebpackPlugin() //---------- 默認是刪除 output.path 指定的文件夾
],
複製代碼

copy-webpack-plugin ---------- 複製文件

  • npm install copy-webpack-plugin -D
const CopyWebpackPlugin = require('copy-webpack-plugin')

plugins: [
  new CopyWebpackPlugin([{
    from: './src/copywebpackplugin', // -------- 將該文件夾中的內容拷貝到dist文件夾中
    to: './' // -------------------------------- 默認是輸出到 output.path 指定的文件夾中
  }])
],
複製代碼

BannerPlugin ---------------- webpack自帶的plugin,用於在js文件開頭註釋一些說明內容

new webpack.BannerPlugin({ banner: ' by woow_wu7'})


對比:webpack自帶插件還有 webpack.ProvidePlugin ------- 將jquery暴露到每一個模塊,爲$符號
// 在每一個模塊中注入$,不須要在每一個模塊中再引入,可使用 webpack.ProvidePlugin插件,再plugins中加入
new webpack.ProvidePlugin({
    $: 'jquery'
}),
複製代碼

webpack-dev-server => before(app) => 實現mock數據

devServer: {
  contentBase: path.join(__dirname, 'dist'),
  port: '5000',
  compress: true,
  open: true,
  // proxy: {
  //   '/api': {
  //     target: 'http://localhost:7000',
  //     pathRewrite: {
  //       '^/api': ''
  //     }
  //   }
  // },
  before(app) { // ------------------------------ before鉤子函數,app即express()
    app.get('/api/user', (req, res) => {
      res.json({
        user: 'woow_wu7'
      })
    })
  }
},


const api = new XMLHttpRequest()
api.open('GET', '/api/user', true) 
// -------------------------------------- 在webpack-dev-sesrver的bofore(app)中已經提供了服務路由並返回數據
// -------------------------------------- 因此這裏能夠直接訪問
api.onload = function() {
  const res = api.response
  console.log(res, 'res')
}
api.send()

複製代碼

resolve 解析 -------------------- alias 和 extensions

  • resolve.alias :建立 import 或者 export 的別名
  • resolve.extensions : 自動解析肯定的擴展
    • 注意:( resolve.extenstions ) 是一個 ( 數組 ),成員是 ( .後綴的字符串 )
    • 注意:import SomeFile from "./somefile.ext",要想正確的解析,一個包含「*」的字符串必須包含在數組中。
    • 應用:在引入文件時,省略後綴 ( 優先級是從左往右 )
resolve.alias
resolve.extensions

設置:
resolve: {
  alias: {
    Imageff: path.resolve(__dirname, 'src/images'), // 用 Imageff 代替 images文件夾的絕對路徑
  },
  extensions: ['.js', '.css', '.less', '*'] 
  // ------------------------------------------------- import時省略後綴時,先找.js文件,再找.css文件
  // ------------------------------------------------- 注意:'*' 表示全部類型的文件
},

使用:
import imagex from 'Imageff/1.jpg'
複製代碼

webpack.DefinePlugin ---------- 定義環境變量,即建立一個在編譯時能夠全局配置的常量

(1)
new webpack.DefinePlugin({ // ----------------------- new webpack.DefinePlugin()
  DEV: JSON.stringify('DEV'), // -------------------- ( JSON.stringify('DEV') === "'DEV'" )
  BOOLEAN1: true,
  BOOLEAN2: JSON.stringify(true) // ----------------- 這裏BOOLEAN2和上面BOOLEAN1等價
}),
// -------------------------------------------------- 因此數據能夠用JSON.stringify()來操做,傳入什麼類型就返回什麼類型


(2) webpack的插件常有的有:
new webpack.DefinePlugin({ // ----------------------- webpack.definePlugin
  DEV: JSON.stringify('DEV'),
  BOOLEAN1: true,
  BOOLEAN2: JSON.stringify(true)
}),
new webpack.ProvidePlugin({ // ---------------------- webpack.ProvidePlugin
  $: 'jquery'
}),
new webpack.BannerPlugin({ // ----------------------- webpacck.BannerPlugin
  banner: 'by woow_wu7'
})
複製代碼

webapck-merge ------------ 區分不一樣環境

const merge = require('webpack-merge'); // --------------- webpack-merge
const base = require('./webpack.base.js')

module.exports = merge(base, {
  mode: 'developmen',
  devServer: {
    contentBase: './dist',
    port: 8000,
    open: true,
    compress: true
  }
})
複製代碼

noParse

  • module.noParse 若是包沒有其餘的依賴項,則能夠經過noParse使webpack不去解析該包的依賴關係,提升構建速度
  • 因此該包中:不能含有import,require,define等任何的導入機制
  • module.noParse
module: {
    noParse: /jquery|lodash/, // ------ 不去解析jquery或lodash的依賴關係,由於它們倆都沒有依賴其餘庫,從而提升構建速度
    rules: []
}    
複製代碼

webpack.IgnorePlugin

  • resourceRegExp:匹配(test)資源請求路徑的正則表達式。
  • contextRegExp:(可選)匹配(test)資源上下文(目錄)的正則表達式。
  • webpack自帶的插件
    • webpack.IgnorePlugin--------- 忽略引入
    • webpack.DefinePlugin--------- 定義全局常量
    • webpack.ProvidePlugin-------- 暴露包的名字更名
    • webpack.BannerPlugin--------- 打包的js的最前面注入一寫信息字符串
----
webpack.config.js => plugins
new webpack.IgnorePlugin({ 
  // ------------------------------- 表示從 ( moment ) 中引入 ( ./local ) 文件時,將不去引入./local文件夾中的文件
  // ------------------------------- 可是須要使用中文包,因此忽略後,再 ( 手動引入 )
  resourceRegExp: /^\.\/locale$/,
  contextRegExp: /moment$/
});

-----
index.js
import moment from 'moment'
import 'moment/locale/zh-cn'; // ------------------------------------ 手動引入包
moment.locale('zh-cn')
const d = moment("20111031", "YYYYMMDD").fromNow();
console.log(d, 'd')
複製代碼

webpack.DllPlugin 和 webpack.DllReferencePlugin -------- 動態連接庫,單獨打包一些庫

  • webpack.DllPlugin:=> name,path
  • webpack.DllReferencePlugin:=> manifest
  • manifest:清單
------
webpack.config.react.js // ------------------------------------- 專門用來打包react和react-dom
(1)
const path = require('path')
const webpack = require('webpack')
module.exports = {
  mode: 'development',
  entry: {
    react: ['react', 'react-dom']
  },
  output: {
    filename: '_dll_[name].js', // ---------------------------- 打包後是 ( _dll_react.js )
    path: path.resolve(__dirname, 'dist'),
    library: '_dll_[name]', // 打包後,把打包的文件賦值給_dll_[name]變量,全局變量的名稱
    libraryTarget: 'var', // 變量的類型 
  },
  plugins: [
    new webpack.DllPlugin({ // -------------------------------- webpack.DllPlugin 用於生成任務清單,即manifest.json文件
      name: '_dll_[name]',  
      // name 表示暴露到全局的庫的名稱
      // 注意: name 和 output.librry 要一致
      // 注意:name表明的就是打包後生成在dist目錄下的 manifest.json 中的變量值
      path: path.resolve(__dirname, 'dist', '[name].manifest.json')
    })
  ]
}
(2)
打包:使用命令打包:npx webpack --config webpack.config.react.js
生成:
(1) _dll_react.js // ----------------------------------------- 文件的內容會賦值給一個變量 ( _dll_react )
(2) manifest.json文件



------
(3)
webpack.config.js
plugins: [
  new webpack.DllReferencePlugin({ //----------------------------- 引用清單
    manifest: path.resolve(__dirname, 'dist', 'react.manifest.json') //- 引用上面打包生成的 manifest.json 文件
  })
],


-----
(4)
index.js
import react from 'react'
import {render} from 'react-dom'
render(<h1>jsx</h1>, document.getElementById('root'))

(5)


效果:打包main.js從( 1.4M => 500KB )
複製代碼

happypack ------------------------- 多線程打包

module.rules:
{
  test: /\.js$/,
  use: 'happypack/loader?id=js',
  },
}

plugins:
new HappyPack({
    id: 'js',
    use: [{
        loader: 'babel-loader'
    }]
})
複製代碼

代碼分割 optimization.splitChunks

- 抽離公共組件和第三方組件,能夠緩存,則不須要重複加載
- optimization => splitChunks => cacheGroups => vendors和commons
- priority: 是優先的意思
- optimization: {
    minimizer: {}, // 壓縮css和js的配置項
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 1,
          priority: 10,
          minSize: 0,
        },
        vendors: { // vendor是小販的意思
          test: /node_modules/, // 範圍是node_modules中的第三方依賴,注意zhe
          name: 'vendors', // 抽離出來的包的名字
          chunks: 'initial', // 初始化加載的時候就抽離公共代碼
          minChunks: 1, // 被引用的次數
          priority: 11, // priority: 是優先級的意思,數字越大表示優先級越高
          minSize: 0,
        }
      }
    }
  }
複製代碼

@babel/plugin-syntax-dynamic-import --------------------- 實現懶加載

  • npm install @babel/plugin-syntax-dynamic-import 語法動態導入
  • syntax:語法
  • dynamic:動態
(1) 在webpack.config.js中配置,或者在.babelrc中配置
module: {
    rules: [
        {
            test: /\.js$/,
            use: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: {
                            ['@babel/preset-env'],
                            ['@babel/preset-react'],
                        },
                        plugins: {
                            ['@babel/plugin-proposal-decorators', {'legacy': true}],
                            ['@babel/plugin-proposal-class-properties', {'loose': true],
                            ['@babel/plugin-transform-runtime'],
                            ['@babel/plugin-syntax-dynamic-import']
                        }
                    }
                },
                {
                    loader: 'eslint-loader',
                    options: {
                      enforce: 'pre'
                    }
                }
            ]
        }
    ]
}


(2) index.js
const button = document.createElement('button')
button.innerHTML = 'button'
button.addEventListener('click', () => {
  console.log('button clicked')
  import('./dynamic.js').then(res => console.log(res.default, 'res')) // ----------- 語法動態導入,使用jsonp實現
})
document.body.appendChild(button)



(3)
- import只能用在頂部報錯:解決須要安裝 babel-eslint 插件
- 安裝,babel-eslint插件,而且在 .eslintrc.json中作以下配置
- {
    "parser": "babel-eslint",
    "parserOptions": {
        "sourceType": "module",
        "allowImportExportEverywhere": true, // ---------------------- 在全部地方都能import和export
    },
    "rules": {
        "indent": "off"
    },
    "env": {}
}
- https://stackoverflow.com/questions/39158552/ignore-eslint-error-import-and-export-may-only-appear-at-the-top-level
複製代碼

webpack.HotModuleReplacementPlugin 和 webpack.NameModulesPlugin --------- 熱更新

- 熱更新
- new webpack.HotModuleReplacementPlugin() // 熱更新
- new webpack.NameModulesPlugin() // 打印熱更新模塊的路徑
- 1. 首先在 devServer 配置中增長 hot: true,表示開啓熱更新
- 2. 在plugins數組中 new webpack.HotModuleReplacementPlugin() 和 new webpack.NameModulesPlugin()
- 3. 在入口js文件中:

(1)
devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: '5000',
    compress: true,
    open: true,
    hot: true, //-------------------------------------------------------- hot: true 開啓熱更新模式
    // proxy: {
    //   '/api': {
    //     target: 'http://localhost:7000',
    //     pathRewrite: {
    //       '^/api': ''
    //     }
    //   }
    // },
    before(app) {
      app.get('/api/user', (req, res) => {
        res.json({
          user: 'woow_wu7'
        })
      })
    }
  },
  
(2)
 plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      hash: true,
      minify: {
        removeAttributeQuotes: true,
        collapseWhitespace: true,
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/main.css'
    }),
    // new CleanWebpackPlugin(),
    new CopyWebpackPlugin([{
      from: './src/copywebpackplugin',
      to: './'
    }]),
    new webpack.DefinePlugin({
      DEV: JSON.stringify('DEV'),
      BOOLEAN1: true,
      BOOLEAN2: JSON.stringify(true)
    }),
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/local$/,
      contextRegExp: /moment$/,
    }),
    new webpack.ProvidePlugin({
      $: 'jquery'
    }),
    new webpack.BannerPlugin({
      banner: 'by woow_wu7'
    }),
    new webpack.NamedModulesPlugin(), // --------------------- webpack.NameModulesPlugin 打印更新的模塊路徑
    new webpack.HotModuleReplacementPlugin() // -------------- webpack.HotModuleReplacePlugin 熱更新插件
    // new webpack.DllReferencePlugin({
    //   manifest: path.resolve(__dirname, 'dist', 'manifest.json')
    // })
  ],


(3) index.js
import hotNow from './hot';
console.log(hotNow)
if (module.hot) { // ----------------------------------------- 若是hot.module存在,開啓了熱更新
  module.hot.accept('./hot.js', function(){ // --------------- 監聽 './hot.js'的改變,若是改變,執行回調
    const res = require('./hot.js') // ----------------------- 從新加載,並打印最新值
    console.log(res, '熱更新後新的返回值')
  })
}
複製代碼

編寫一個loader

  • loader就是一個 ( 函數 ),第一個參數表示 ( 該loader匹配的文件的源代碼 )
  • 不能寫成箭頭函數,由於須要經過 this 獲取更多 api
  • this.query
    • 如何獲取loader中的配置參數:( options對象 ) this.query指向的就是options對象
    • 若是 loader 中沒有 options,而是以 query 字符串做爲參數調用時,this.query 就是一個以 ? 開頭的字符
      • 注意:this.query已經廢棄,使用 loader-utils 中的 getOptions 來獲取 options 對象
  • loader-utils
    • npm install loader-utils -D
    • 經過loader-utils中的 getOptions 獲取 loader的options配置對象
  • this.callback
    • 參數
      • 第一個參數:err // Error 或者 null
      • 第二個參數:content // string或者buffer,即處理事後的源代碼
      • 第三個參數:sourceMap // 可選,必須是一個能夠被這個模塊解析的 source map
      • 第四個參數:meta //可選,即元數據
      • // www.webpackjs.com/api/loaders…
  • this.async
    • 處理loader中的異步操做
    • this.async()方法返回 this.callback
  • resolveLoader
    • webpack配置項
    • resolveLoader的配置項
      • 代碼:modules: ['node_modules', './src/loaders']
      • 表示:在尋找loader的時候,先去node_modules文件夾中共尋找,沒找到再去'./src/loaders'文件夾中找
  • loader API

最簡單的replace-loader

replace-loader

---
目錄:src/loaders/replace-loader.js
module.exports = function(source) { // --------------------- loader就是一個函數,參數是源碼
  return source.replace('hello', 'hi!')
}

目錄:webpack.config.js
module: {
  rules: [{
      test: /\.js$/,
      use: [path.resolve(__dirname, './src/loaders/replace-loader.js')]
  }]
}
複製代碼

升級版

目錄:webpack.config.js
(1)
module.exports = {
    resolveLoader: { // ---------------------------- resolveLoader
      modules: ['node_modules', './src/loaders/'] // 表示在尋找loader時,先去node_modules中找,再去loaders文件夾中找
    },
]
(2)
{
  loader: 'replace-loader', // -------------------- 加載replace-loader,即文件名
  options: {
    name: 'hi!!!!!!!!@!!!!!!' // ---------------------- options的name屬性
  }
}


目錄:src/loaders/replace-loader
const loaderUtils = require('loader-utils') // ---------------- loader-utils
module.exports = function(source) {
  console.log(this.query) // options配置對象

  const options = loaderUtils.getOptions(this) // ------------- loaderUtils.getOptions(this) 獲取 options
  const callback = this.async() // ---------------------------- 處理異步loader, this.async()返回this.callback
  setTimeout(function() {
    const result = source.replace('hello', options.name)
    callback(null, result)
  }, 1000)
}
複製代碼

資料

個人簡書:www.jianshu.com/p/1fc4640f4…
webpack優化:juejin.im/post/5c7389…
process.env www.jianshu.com/p/c8f9c61c2…vue

相關文章
相關標籤/搜索