霖呆呆向你發起了多人學習webpack-構建方式篇(2)

霖呆呆的webpack之路-構建方式篇

前言

你盼世界,我盼望你無bug。Hello 你們好!我是霖呆呆!javascript

什麼?!你還想要"呆妹"出來給你講webpack?!小夥子,你的想法很危險❌啊。html

不可能的,下次想要見到"她"可能要等到呆呆5000粉的時候吧😒。在這以前我毫不可能再女裝👚了 😊。前端

(因此請醒醒吧,你看到的那麼可愛的萌妹是一個帥哥!這個帥哥他迷惑了你!固然我現實生活中不叫帥哥哈,由於我在廣東,因此他們通常都叫我靚仔)vue

另外關於「霖呆呆的webpack之路系列」的教材案例我更新了github的地址哦,以前那個太亂了我給刪了,如今全部的教材案例都是同一個項目,不過不一樣的分支上能夠下載單獨的案例,主幹上是全部的案例。具體下載方式請仔細閱讀github上的README。(github.com/LinDaiDai/w…java


霖呆呆向你發起了多人學webpacknode

請選擇:☑️接受 ⭕️拒絕webpack


webpack系列介紹

此係列記錄了我在webpack上的學習歷程。若是你也和我同樣想要好好的掌握webpack,,那麼我認爲它對你是有必定幫助的,由於教材中是以一名webpack小白的身份進行講解, 案例demo也都很詳細, 涉及到:git

  • 基礎篇
  • 構建方式篇(本章)
  • 優化篇
  • loader篇
  • 配置篇

建議先mark再花時間來看。github

(其實這個系列在很早以前就寫了,一直沒有發出來,當時還寫了一大長串前言可把我感動的,想看廢話的能夠點這裏:GitHub地址,不過如今讓咱們正式開始學習吧)web

全部文章webpack版本號^4.41.5, webpack-cli版本號^3.3.10

webpack3中,webpack自己和它的CLI都是在同一個包中,但在第4版中,二者分開來了,也是爲了讓咱們更好地管理它們。

經過閱讀本篇文章你能夠學習到:

  • webpack --watch
  • webpack-dev-server 工具
  • webpack-dev-middle 工具, 以及配合express搭建本地web服務器
  • webpack-merge 構建不一樣的環境
  • process.env.NODE_ENV 的基本使用
  • webpack.DefinePlugin 插件指定 NODE_ENV

1、幾種開發工具

每次要編譯代碼時,手動運行 npm run build 就會變得很麻煩。

不知道你有沒有使用過相似於vue-cli這樣的腳手架工具, 在使用它們的時候, 每次只要執行npm run start這樣的指令就能夠建立一個本地的web服務器, 而後打開一個例如localhost:8080這樣的端口頁面, 同時還有熱更新等功能.

其實這些功能的實現都是vue-cli內部使用了webpack.

webpack中有幾個不一樣的選項,能夠幫助你在代碼發生變化後自動編譯代碼.

(第一節教材案例GitHub地址: LinDaidai/webpack-example/tree/webpack-server ⚠️:請仔細查看README說明)

webpack's Watch Mode(觀察者模式)

觀察者模式, 只須要在package.json裏配置一個腳本命令:

"scripts": {
  "watch": "webpack --watch"
}
複製代碼

使用npm run watch命令以後, 會看到編譯過程, 可是不會退出命令行, 而是實時監控文件.

好比你在從新修改了本地的代碼並保存後, 它會從新進行編譯, 不須要咱們手動再執行編譯指令, 缺點是你須要手動刷新頁面才能看到更改效果.

(--watch也能夠簡寫爲-w)

webpack-dev-server

使用webpack-dev-server會爲你提供一個簡單的web服務器, 它的做用就是監聽文件的改變並自動編譯, 同時會自動刷新頁面. 比觀察者模式厲害.

使用步驟:

  • 安裝: $ npm i --save-dev webpack-dev-server

  • 添加腳本命令: "start": "webpack-dev-server --open"

使用此指令效果:

不會生成dist文件夾, 而是開啓了一個本地的web服務器localhost:8080

每次修改了本地代碼以後, 都會從新自動編譯, 並刷新頁面

其它配置項:

webpack-dev-server也有不少配置項能在webpack.config.js中配置

只須要在devServer裏進行配置, 例如:

module.exports = {
    devServer: {
        contentBase: './dist', // 告訴服務器從哪裏提供內容
        host: '0.0.0.0', // 默認是 localhost
        port: 8000, // 端口號, 默認是8080
        open: true, // 是否自動打開瀏覽器
        hot: true, // 啓用 webpack 的模塊熱替換特性
        hotOnly: true // 當編譯失敗以後不進行熱更新
    }
}
複製代碼

若是你使用了這個功能以後, 你就會發現, 它就有點vue-cli的樣子了.

更多關於devServer的配置能夠查看這裏: 開發中Server

webpack-dev-middleware

基本使用

webpack-dev-middleware 是一個容器(wrapper),它能夠把 webpack 處理後的文件傳遞給一個服務器(server)。

webpack-dev-server 可以開啓一個本地的web服務器, 就是由於在內部使用了它,可是, 它也能夠做爲一個包來單獨使用.

這裏我就以官方的案例來進行講解.

使用webpack-dev-middleware配合express server來介紹它的功能.

express是一個很精簡的Node.js開發框架,若是你以前沒用過也不要緊,使用起來很簡單。)

先來講下個人需求, 我想要實現一個這個功能:

  • 配置一條script指令讓它能運行一個本地web服務器(也就是可以在localhost: 3000中查看頁面)
  • 每次修改本地代碼可以從新編譯
  • 可是不會自動刷新頁面
  1. 安裝所需的依賴:
$ npm i --save-dev webpack-dev-middleware express
複製代碼
  1. 在項目的根目錄下建立一個server.js文件用來編寫本地服務:
// server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')

const app = express()
const config = require('./webpack.config')
const compiler = webpack(config)
// 把 webpack 處理後的文件傳遞給一個服務器
app.use(webpackDevMiddleware(compiler))

app.listen(3000, function() {
    console.log('Example app listening on port 3000!\n');
})
複製代碼
  1. package.json裏配置指令運行server.js:
{
    "scripts": {
        "server": "node server.js"
    }
}
複製代碼

publicPath配置項

在學習這裏的時候, 我順便也瞭解到了webpack.config.js 中output的另外一個屬性publicPath.

開始看文檔 output.outputPath的時候沒太看懂.

後來我結合webpack-dev-middleware來試了一下它.

首先修改一下webpack.config.js的配置:

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

module.exports = {
    entry: {
        app: './src/index.js',
        print: './src/print.js'
    },
    devtool: 'inline-source-map', // 僅開發環境報錯追蹤
    plugins: [
        new CleanWebpackPlugin({
            cleanAfterEveryBuildPatterns: ['dist']
        }),
        new HtmlWebpackPlugin({
            title: 'Webpack Output2',
            filename: 'index.html',
            template: 'src/index.html'
        })
    ],
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
+ publicPath: '/assets/'
    }
}
複製代碼

而後修改一下server.js:

// server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')

const app = express()
const config = require('./webpack.config')
const compiler = webpack(config)
// 把webpack 處理後的文件傳遞給一個服務器
app.use(webpackDevMiddleware(compiler 
+ ,{
+ publicPath: config.output.publicPath
+ }
))

app.listen(3000, function() {
    console.log('Example app listening on port 3000!\n');
})
複製代碼

保存上面👆兩個文件, 而後從新執行npm run server, 打開localhost:3000 會發現頁面顯示的是:

Cannot GET /
複製代碼

你須要打開localhost:3000/assets/才能看到正確的頁面.

而且若是項目裏有對資源的引用的話, 也會自動加上publicPath的前綴:

icon.png => 變爲 /assets/icon.png
複製代碼

此選項指定在瀏覽器中所引用的「此輸出目錄對應的公開 URL」。

⚠️:

若是沒有配置output.publicPathwebpack-dev-middlewarepublicPath, 則默認都會是"",以根目錄做爲配置項。

若是配置了output.publicPath, 則webpack-dev-middleware中的publicPath也要和它同樣才行。

2、不一樣環境的構建

開發環境和生產環境的構建目標差別是很是大的.

  • 開發環境中, 咱們可能有實時從新加載(live reloading) 、熱模塊替換(hot module replacement)等能力
  • 生產環境中, 咱們更加關注更小的bundle(壓縮輸出), 更輕量的source map, 還有更優化的資源等.

因此爲了遵循邏輯分離, 咱們能夠爲每一個環境編寫彼此獨立的webpack配置.

雖然說是想要編寫各自獨立的配置, 可是確定也有一些公用的配置項, 咱們能夠將這些公用的配置項提取出來, 而後不一樣的配置寫在不一樣的文件中.

(第二節教材案例GitHub地址: LinDaidai/webpack-example/webpak-merge ⚠️:請仔細查看README說明)

webpack-merge

最終, 爲了將這些配置項合併在一塊兒, 咱們須要用到webpack-merge工具.

首先安裝這個工具:

$ npm i --save-dev webpack-merge
複製代碼

而後讓咱們將本來的webpack.config.js拆開, 編寫成三個不一樣的webpack配置文件:

webpack-demo
  |- package.json
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
  |- /dist
  |- /src
    |- index.js
    |- math.js
  |- /node_modules
複製代碼

webpack.common.js:

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

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'webpack bundle'
        })
    ]
}
複製代碼

webpack.dev.js:

const merge = require('webpack-merge')
const commonConfig = require('./webpack.common')

module.exports = merge(commonConfig, {
    devtool: 'inline-source-map', // 錯誤追蹤
    devServer: { // 設置 webpack-dev-server 監聽的文件
        contentBase: './dist'
    }
})
複製代碼

webpack.prod.js:

const merge = require('webpack-merge')
const commonConfig = require('./webpack.common')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')

module.exports = merge(commonConfig, {
    plugins: [
        new UglifyJSPlugin() // 壓縮輸出
    ]
})
複製代碼

能夠看到, webpack-merge的功能就是將多個webpack的配置合併成一個.

如今讓咱們再來配置一下package.json的腳本命令:

package.json:

{
    "name": "webpack-bundle",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start": "webpack-dev-server --open --config webpack.dev.js",
        "build": "webpack --config webpack.prod.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "clean-webpack-plugin": "^3.0.0",
        "html-webpack-plugin": "^3.2.0",
        "uglifyjs-webpack-plugin": "^2.2.0",
        "webpack": "^4.41.5",
        "webpack-cli": "^3.3.10",
        "webpack-dev-server": "^3.10.3",
        "webpack-merge": "^4.2.2"
    }
}
複製代碼
  • 執行npm run start爲開發環境, 會自動打開localhost:8080頁面而且有自動重載功能
  • 執行npm run build爲生產環境, 會打包生成dist文件夾, 且bundlejs爲壓縮事後的代碼.

process.env.NODE_ENV

基本用法

process.env.NODE_ENV的做用主要是幫咱們判斷是開發環境(development)仍是生產環境(production).

技術上講,NODE_ENV 是一個由 Node.js 暴露給執行腳本的系統環境變量。

  1. 你能夠在任何src的本地代碼中引用到它:
// print.js
export function print() {
    console.log(process.env.NODE_ENV) // development 或者 prodution
}
複製代碼
  1. 可是你在webpack.config.js中卻獲取不到它, 打印出來是undefined.因此像如下代碼是不能像預期同樣實現的:
process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'
複製代碼

webpack.DefinePlugin插件

以前介紹過了, 咱們是不能在webpack.config.js中獲取到process.env.NODE_ENV的值的, 可是咱們可使用webpack內置的DefinePlugin插件來修改這個變量.

例如我在webpack.prod.js中的配置:

+ const webpack = require('webpack');
  const merge = require('webpack-merge');
  const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
  const commonConfig = require('./webpack.common.js');

  module.exports = merge(commonConfig, {
    devtool: 'source-map',
    plugins: [
      new UglifyJSPlugin({
        sourceMap: true
- })
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify('production')
+ })
    ]
  });
複製代碼

使用webpack.DefinePlugin()方法修改了process.env.NODE_ENV.

你能夠設置成JSON.stringify('production'), 也能夠設置成:

new webpack.DefinePlugin({
      'process.env': {
          'NODE_ENV': `"production"`
      }
  })
複製代碼

命令行配置模式mode

除了使用webpack.DefinePlugin插件來修改環境變量的模式, 還能夠在命令行中修改它:

webpack --mode=production
或者
webpack --mode=development
複製代碼

使用了--mode設置環境變量模式, 在本地代碼上獲取到的process.env.NODE_ENV的值就是mode的值.

不過若是你同時在命令行中設置的--mode, 又使用了webpac.definePlugin插件, 後者的優先級高點.

命令行傳遞環境變量

若是咱們在命令行中經過--env來設置一些變量值, 這些變量值能使咱們在webpack.config.js的配置中訪問到.

在webpack命令行配置中, 經過設置 --env 可使你根據須要,傳入儘量多的環境變量

例如我新建了一個命令行:

{
    "scripts": {
        "start": "webpack-dev-server --open --config webpack.dev.js",
        "build": "webpack --config webpack.prod.js",
+ "local": "webpack --env.custom=local --env.production --progress --config webpack.local.js"
    }
}
複製代碼

拆開來看:

  • --env.custom=local 給環境變量中設置一個自定義的屬性 custom, 它的值爲local
  • --env.production 設置env.production == true(這裏的env並不會影響process.env)
  • --progress 打印出編譯進度的百分比值
  • --config webpack.local.jswebpack.local.js中的內容執行webpack構建

同時我在項目根目錄下建立一個wepack.local.js:

const commonConfig = require('./webpack.common')
const merge = require('webpack-merge')

module.exports = env => {
    console.log('custom: ', env.custom) // 'local'
    console.log('Production: ', env.production) // true
    return merge(commonConfig, {})
}
複製代碼

能夠看到它與廣泛的webpack.config.js的區別在於, 它導出的是一個函數, 且這個函數中能訪問env環境變量.

這樣咱們就能夠將在命令行中設置的變量獲取到了.

命令行傳遞環境變量判斷NODE_ENV

還記得咱們以前說, 在webpack.config.js中是不能獲取到環境變量process.env.NODE_ENV , 也就是不能作如下判斷:

process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'
複製代碼

可是如今咱們在命令行裏傳遞一個變量進去, 好比叫作NODE_ENV, 這樣就能夠在webpack.config.js裏做區分了.

讓咱們在根目錄下建立一個名爲webpack.combine.js的配置文件:

webpack.combine.js:

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

module.exports = env => {
    return {
        entry: './src/index.js',
        output: {
            filename: env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                title: '合併成同一個webpack配置'
            })
        ]
    }
}
複製代碼

咱們能夠看到ouput.filename,能夠經過NODE_ENV來判斷.

因此我須要在package.json 中進行參數的傳遞:

{
    "name": "webpack-bundle",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "webpack-dev-server --open --config webpack.dev.js",
        "build": "webpack --config webpack.prod.js",
        "local": "webpack --env.custom=local --env.production=false --mode=development --progress --config webpack.local.js",
+ "combine-dev": "webpack --env.NODE_ENV=development --config webpack.combine.js",
+ "combine-prod": "webpack --env.NODE_ENV=production --config webpack.combine.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "clean-webpack-plugin": "^3.0.0",
        "html-webpack-plugin": "^3.2.0",
        "lodash": "^4.17.15",
        "uglifyjs-webpack-plugin": "^2.2.0",
        "webpack": "^4.41.5",
        "webpack-cli": "^3.3.10",
        "webpack-dev-server": "^3.10.3",
        "webpack-merge": "^4.2.2"
    }
}
複製代碼

如今分別執行combine-devcombine-prod, 能夠看到生成的bundle又不一樣的效果.

combine-dev生成的js文件是main.bundle.js

combine-prod生成的js文件是main.a79eb0c94212b905d48b.bundle.js

可是有一點須要注意的是這裏的env.NODE_ENV並非process.env.NODE_ENV, 因此它並不能改變process.env.

也就是說無論你經過哪一種方式生成的頁面, 你在頁面中獲取到的process.env.NODE_ENV都仍是production.

第二節總結

  • 能夠安裝webpack-merge工具幫助咱們將多個配置文件合併成一個
  • webpack.config.js獲取不到環境變量process
  • 能夠經過webpack.DefinePlugin插件幫助咱們修改process.env的值
  • 還能夠經過命令行CLI中的 --mode 來修改環境變量的模式
  • 如果webpack.config.js導出的是一個函數, 則容許咱們在命令行中用 --env 傳遞環境變量

案例地址

第一節:github.com/LinDaiDai/w…

第二節:github.com/LinDaiDai/w…

注意⚠️:其實「霖呆呆的webpack之路系列」全部的教材案例都是同一個項目,不過不一樣的分支上能夠下載單獨的案例,主分支上是全部的案例。具體下載方式請仔細閱讀github上的README。

參考文章

知識無價,支持原創。

參考文章:

後語

喜歡霖呆呆的小夥還但願能夠關注霖呆呆的公衆號 LinDaiDai 或者掃一掃下面的二維碼👇👇👇.

我會不定時的更新一些前端方面的知識內容以及本身的原創文章🎉

你的鼓勵就是我持續創做的主要動力 😊.

相關推薦:

《全網最詳bpmn.js教材》

《【建議改爲】讀完這篇你還不懂Babel我給你寄口罩》

《【建議星星】要就來45道Promise面試題一次爽到底(1.1w字用心整理)》

《【建議👍】再來40道this面試題酸爽繼續(1.2w字用手整理)》

《【何不三連】比繼承家業還要簡單的JS繼承題-封裝篇(牛刀小試)》

《【何不三連】作完這48道題完全弄懂JS繼承(1.7w字含辛整理-返璞歸真)》

《【精】從206個console.log()徹底弄懂數據類型轉換的前世此生(上)》

相關文章
相關標籤/搜索