webpack從0到1使用指南

文章同步發表在:博客地址javascript

爲何要用webpack

關於爲何要使用webpack,我比較認同的一種說法是:css

webpack能夠很好地管理你開發中遇到的各類HTML、JS、CSS以及各類圖片資源文件,同時對應不一樣的資源,webpack還提供了對應的Loaders將其進行轉化爲適用於瀏覽器使用的格式html

如何從0開始上手webpack

後會無期裏面,阿呂說過這麼一句話:"你連世界都沒有觀過,哪裏來的世界觀",在咱們實際的項目開發中,好比你用的是vue,那麼vue已經有很好的腳手架工具(vue-cli)供你使用了,或者有的開發團隊,會有技術Leader專門預先作好相關的模板,方便後來新加入的成員儘快上手項目,可是隨着你開發的項目越多,你可能會越不注意到那些最基本的東西,好比說這個模板究竟是如何搭建的,或者說讓你本身來搭建一個模板給其餘人用,是否也能作到如此的簡單易上手,我想這是每個想要在前端路上進階的人須要考慮的問題。前端

最好的方式就是本身試着去搭一個最簡單的模板,從0到1的過程是最痛苦的,可是1到2或者說2到3都是水到渠成的事,下面就看看怎麼開始從0搭建一個基於webpack的vue的開發環境vue

建立項目

注:使用的webpack版本爲^3.10.0 首先經過webstorm或者你手上的其餘IDE建立一個空的項目,我這裏暫且叫proWebpack,建立好後是這個樣子的:java

文件目錄

由於咱們經過npm來管理包,而後每一個項目的根目錄下都會有一個package.json文件來管理項目的配置信息,包括名稱、版本、許可證等元數據以及記錄所需的各類模塊,包括 執行依賴,和開發依賴,咱們能夠經過在命令控制檯用npm init命令建立一個package.json文件,可是這樣很麻煩,由於這樣npm會經過命令行問答的方式來初始化並建立package.json文件,爲了方便起見咱們使用npm init -y,這樣npm就會使用一些默認值進行初始化:node

運行結果

ok如今咱們獲得一個package.json文件,包含了一些簡單的信息好比name、version等字段,接下來,由於咱們是想搭建一個用於vue開發環境的webpack目錄結構,到目前爲止還沒看到vue的影子,依然是在命令行使用npm install vue --save,這裏注意package.json中的依賴包有dependenciesdevDependencies兩種,這兩種的區別:dependencies 表示咱們要在生產環境下使用該依賴,devDependencies 則表示咱們僅在開發環境使用該依賴,咱們install時候使用--save會把包安裝在dependencies 下面,因此執行完上面的命令你能夠看到這樣的目錄結構:webpack

{
  "name": "proWebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  }
}
複製代碼

而後在devDependencies下面安裝webpack,再次提醒,因爲webpack4變化較大,這裏使用^3.10.0版本的webpack: npm install -D webpack@3.10.0,此時目錄結構以下:git

{
  "name": "proWebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  },
  "devDependencies": {
    "webpack": "^3.10.0"
  }
}

複製代碼

建立入口文件

接下來建立入口文件,在根目錄下建立一個index.html做爲啓動頁面,一個webpack.config.js做爲webpack配置文件(實際項目中這裏會有webpack配置文件,分別用於開發環境和生產環境,這裏簡便起見就用一個配置文件),新建一個src目錄,在該目錄下新建一個index.js做爲打包入口文件:github

proWebpack
├─ index.html 啓動頁面
├─ package-lock.json
├─ package.json 包管理
├─ src
│    └─ index.js 入口文件
└─ webpack.config.js  webpack配置文件
複製代碼

爲了更接近vue-cli建立出來的模板,咱們還須要建立一個Vue實例提供給入口文件的el掛載,這個Vue文件很簡單長這樣:

<template>
  <div>proWebpack</div>
</template>

複製代碼

而後寫好入口文件:

import Vue from 'vue'
import App from '../App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})
複製代碼

同時別忘記在Index.html裏面要提供一個供vue實例掛載的HTMLElement實例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>

複製代碼

這樣準備工做作得差很少了,下面作最核心的部分

webpack.config.js的編寫

webpack有這麼幾個核心的概念:

  • entry 入口起點,webpack會找出入口起點的直接或間接依賴項,將他們進行處理輸出爲咱們稱之爲bundle的文件中
  • output 輸出路徑,告訴 webpack 在哪裏輸出它所建立的 bundles以及如何給這些buldles命名
  • loader 由於webpack自己只能理解javascript文件,因此當咱們要用vue或者React的時候可使用相關的loader(好比咱們熟知的vue-loader)來將其轉換成webpack 可以處理的有效模塊
  • plugins 擴展插件,能夠用於打包的優化和壓縮,這個可能要結合實際使用更好理解
  • module 模塊 webpack中的一切你均可以理解爲模塊
  • chunk 代碼塊,一個 chunk 由多個模塊組合而成,用於代碼合併與分割。

下面開始配置,由於全部輸出文件的目標路徑必須是絕對路徑,因此這裏要用到使Node.js 的 path 模塊

// 引入webpack和path模塊
const path = require('path')
const webpck = require('webpack')
複製代碼

簡單配置文件的entry和output

const path = require('path')
const webpck = require('webpack')
const productionPath = require('./package.json').name

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

複製代碼

這裏代碼應該不難理解,就是從index.js這個入口進去,把直接或間接依賴項打包輸出dist文件夾目錄下, 怎麼運行呢,咱們須要在package.json的scripts對象裏面添加咱們本身的build命令:

//  這裏用最簡單的命令
"build": "webpack -w",
複製代碼

由於咱們的webpack配置文件就叫webpack.config.js,默認狀況下,webpack在執行的時候會搜索當前目錄webpack.config.js 文件執行,實際開發中咱們會使用 --config 選項來指定配置文件來對開發環境和生產環境的配置作出區分

直接在控制檯npm run build,果不其然報錯了:

ERROR in ./App.vue
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| <template> | <div>proWebpack</div> | </template>
 @ ./src/index.js 2:0-28
複製代碼

很明顯,webpack告訴咱們他無法識別vue文件,須要咱們提供一個相關的loader來處理成webpack能夠識別的文件,這裏先npm install vue-loader -D,而後回到webpack.config.json配置loader:

const path = require('path')
const webpck = require('webpack')

module.exports = {
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    // 模塊配置
    rules: [
      //模塊規則(配置 loader、解析器等選項)
      {
        // 這裏是匹配條件,每一個選項都接收一個正則表達式或字符串
        // test是必須匹配選項
        // exclude 是必不匹配選項(優先於 test 和 include)
        // 對選中後的文件經過 use 配置項來應用 Loader,能夠只應用一個 Loader 或者按照從後往前的順序應用一組 Loader,同時還能夠分別給 Loader 傳入參數。
        test: /\.vue$/,
        exclude: /node_modules/,
        use: {loader: "vue-loader"}
      }
    ]
  }
}

複製代碼

配置好以後再run一遍,咦,又報錯,咱們看下主要的報錯信息:

ERROR in ./App.vue
Module build failed: Error: Cannot find module 'vue-template-compiler'
    at Function.Module._resolveFilename (module.js:527:15)
    at Function.Module._load (module.js:476:23)

複製代碼

cannot fount那就是須要咱們去install唄,可是爲何要下這個模塊,我找到一個比較靠譜的說法:

其中 vue-template-compiler 是 vue-loader 的 peerDependencies,npm3 不會自動安裝 peerDependencies,然而 vue-template-compiler 又是必備的,那爲何做者不將其放到 dependencies 中呢?有人在 github 上提過這個問題,我大體翻譯一下做者的回答(僅供參考):這樣作的緣由是由於沒有可靠的方式來固定嵌套依賴的關係,怎麼理解這句話?首先 vue-template-compiler 和 vue 的版本號是一致的(目前是同步更新),將 vue-template-compiler 指定爲 vue-loader 的 dependencies 並不能保證 vue-template-compiler 和 vue 的版本號是相同的,讓用戶本身指定版本才能保證這一點。查看做者的回答(英文) 。若是二者版本不一致,運行時就不會錯誤提示。

那隻須要咱們npm i vue-template-compiler -D一下,而後在跑一遍build腳本,不出意外你應該能在項目根目錄看到一個dist文件夾,把這個文件夾裏面的js文件引入啓動頁面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
<script src="/dist/bundle.js"></script>
</body>
</html>
複製代碼

接下來咱們要訪問這個頁面,這裏用到webpack提供的devsever來調試咱們的頁面, DevServer 會啓動一個 HTTP 服務器用於服務網頁請求,同時會幫助啓動 Webpack ,並接收 Webpack 發出的文件更變信號,經過 WebSocket 協議自動刷新網頁作到實時預覽。這就是爲何你用vue-cli搭建出來的腳手架能夠作到頁面實時渲染,配置這個也很簡單,在webpack.config.js裏面添加以下配置:

devServer: {
    port: 3000, // 端口號
  }
複製代碼

其實webpack-dev-server能夠配置不少參數,這裏不過多展開,接下來須要修改下咱們的build腳本:

"build": "webpack-dev-server"
複製代碼

別忘了npm install一下webpack-dev-server,最後執行npm run build看到控制檯報出成功信息並告訴你你的項目正運行在localhost:3000:

$ npm run build

> proWebpack@1.0.0 build D:\proWebpack
> webpack-dev-server --open

Project is running at http://localhost:3000/
webpack output is served from /

複製代碼

訪問localhost:3000看到以下顯示的話,恭喜你,webpack配置如何從0到1你應該已經清楚了:

渲染結果

更進一步

咱們知道了怎麼初步配置webpack還遠遠不夠,實際開發中咱們會遇到更多樣的狀況,好比說 當咱們只有一個輸出文件的時候咱們能夠在output寫死,就像咱們上面寫的:

output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  }
複製代碼

可是當咱們有多個文件輸出的時候,一個個去寫是一個費力不討好的作法,這個時候就要用到webpack內置的變量了,咱們能夠這麼寫:

output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
  }
複製代碼

這樣會賦予每個bundle惟一的名稱,而且在每次構建過程當中,生成惟一的 hash ,這時候稍微改動一下腳本文件:

"scripts": {
    "start": "webpack-dev-server",
    "build": "webpack -w",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
複製代碼

以前寫一塊兒是爲了方便理解, 如今我把跑本地服務器和打包的腳本拆開寫,更語義化,通常正常開發也是這麼作的,先跑一下build,你會發現此時dist文件夾下面生成了新的打包文件index.8ad46f4fb5c8385db614.js,而後把這個文件一樣引入index.html,跑start腳本發現結果也是照常輸出,可是又有了新的問題,若是咱們每次build以後都要手動引入帶着一長串hash的js文件也是很蠢的事情,因此這裏咱們能夠用上一個plugin叫作html-webpack-plugin,這個插件主要有兩個做用: 一、在每次編譯完成以後動態爲html文件引入外部資源(script、link),對於我me你這種帶hash的文件名來講無疑是極爲方便的 二、能夠制定一個模板的html文件,html-webpack-plugin能夠根據這個模板來生成html入口文件

下面看下如何使用:

// 引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
    //省略若干代碼
 plugins: [
    new HtmlWebpackPlugin({
      filename: './index.html', // 生成的入口文件的名字,默認就是index.html
      template: './index.tpl.html',// 有時候,插件自動生成的html文件,並非咱們須要結構,咱們須要給它指定一個模板,讓插件根據咱們給的模板生成html
      inject: 'body',// 有四個選項值 true, body, head, false----true:默認值,script標籤位於html文件的 body 底部 body:同true head:插入的js文件位於head標籤內 false:不插入生成的js文件,只生成一個純html
    })
  ],
複製代碼

用到的index.tpl.html只是和以前的index.html做了一點小小的區分,讓人知道這是模板生成的:

// index.tpl.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>這是模板生成的</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>
複製代碼

方便起見咱們在package.json在添加一個clean的腳本,每次build以前先刪除以前生成的dist文件:

"scripts": {
    "start": "webpack-dev-server",
    "build": "npm run clean && webpack -w",
    "clean": "rimraf dist", // 添加clean腳本
    "test": "echo \"Error: no test specified\" && exit 1"
  }
複製代碼

而後run build一下:

模板生成的結果
咱們發現這個時候的入口文件已是根據模板自動生成的了,並且自動把生成的js文件添加到了入口文件裏面,同時npm run start命令看到的界面也是使用模板生成的頁面,這就大功告成了。

最後

其實這裏還有一點沒有提到的就是css的處理,除了css-loader咱們日常開發還會用到一些css預處理器如scss,還有postcss等樣式處理工具,這裏的處理其實也不難,感興趣的朋友能夠本身嘗試一下,這裏因爲篇幅緣由(我想偷懶)就不過多展開了。

相關文章
相關標籤/搜索