自己動手 webpack 打包 vue 單檔案元件

2021-04-12 09:00:47

1. 為什麼要了解這些

vue 的腳手架整合了 webpack,並進行了大量的預設設定

我們只需要按照框架的要求編寫程式碼,打包就好了

慢慢的我們就成了傻瓜,不知道這其中到底發生了什麼,一旦遇到問題,不知道該如何思考,因為你可能都不清楚它的運作原理

更何況,看尤大的思路,將來很可能使用腳手架建立專案時,打包工具不再是預設的,而是讓你 webpack 和 vite 二選一

所以,我們就更需要對打包過程有一個大概的瞭解

下面我們就手動實現這一點

2. 動手實踐

SPA 和元件化密不可分,元件化讓SPA成為可能,SPA促使元件化程式設計的落地

vue 的單檔案元件就是具體的體現

2.1 搭建目錄結構

建立專案資料夾 webpack-demo

執行如下命令進行初始化

npm init -y

根目錄下新建 src 目錄

src 目錄下新建 index.html,main.js

index.html 程式碼

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app"></div>
  <!-- 引入打包之後的檔案,先不用理解 -->
  <script src="../dist/bundle.js"></script>
</body>

</html>
  • id=app 的div 是將來 vue 範例的掛載點
  • bundle.js 是打包生成的檔案,先不同管它

src 目錄下新建 Home.vue

<template>
  <div class="home">
    <h1>{{ msg }}</h1>
  </div>
</template>
<script>
export default {
  data: function () {
    return {
      msg: 'Home'
    }
  },
  methods: {

  },
  computed: {

  },
  watch: {

  }
}
</script>
<style scoped>
h1 {
  color: red;
}
</style>

當前目錄結構如下

 

2.3 安裝和設定webpack

安裝 webpack

npm i webpack webpack-cli -D

根目錄下新建 webpack.config.js,設定打包模式、入口和出口檔案

const path = require('path')
module.exports = {
  mode: 'development',
  entry: path.join(__dirname, 'src', 'main.js'), // 打包的入口檔案
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  }  
}

package.json 中新增打包命令

2.4 編寫打包入口檔案

在 src/main.js 中編寫程式碼

import Vue from 'vue'
import Home from './Home.vue'

let vm = new Vue({
  el: '#app', // 指定當前vue範例的掛載點,也就是將index.html 中的id=app 的dom作為掛載點
  // 將 Home 元件中的模板和樣式渲染到 index.html 頁面中,具體來說,
  // 就是用模板中的內容替換 index.html 中的 id=app 的元素,也會將 css 放到頁面中
  render: h => h(Home)
})

執行打包命令

npm run dev

報錯

錯誤解決見下一節

2.5 安裝相關載入器(loader)

上面錯誤原因在於 webpack 預設只能打包 js 檔案,對於 .vue 檔案不認識

所以需要安裝能夠處理 vue 檔案的 loader

這個 loader 不能在 webpack 檔案中找,因為 webpack 與 vue 本身是沒有關係的

webpack 只是一個打包工具,預設可以處理 js 檔案,並且提供了一些通用的 laoder,比如

  • css-loader 處理 css 檔案的打包工作
  • url-loader 處理圖片字型等靜態資原始檔的打包工作

所以,vue 官方提供了能夠處理 vue 檔案的載入器

https://vue-loader.vuejs.org/zh/

安裝

npm install -D vue-loader vue-template-compiler

在webpack 中設定

 

 

錯誤資訊

上面錯誤是因為元件中有 css 程式碼,需要對應的 loader

安裝 loader

npm i style-loader css-loader -D

設定規則

module: {
    rules: [
      { test: /\.vue$/, loader: 'vue-loader' },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },

重新執行 

npm run dev

打包成功

執行 index.html

2.6 多元件如何切換

src 目錄下新建 About.vue

<template>
  <div class="about">
    <h2>關於我們</h2>
  </div>
</template>
<script>
export default {

}
</script>
<style scoped>
h2 {
  color: orange;
}
</style>

希望執行 index.html 的時候展示 About 元件的內容,怎麼辦?

可以修改 main.js

重新打包 

npm run dev

index.html

 

雖然實現了,但很顯然,這種實現方案是不現實的

2.7 使用路由實現元件切換

其實,元件切換的邏輯應該是這樣的

  • 首先,我們是一個 SPA 應用,只有一個 index.html 作為應用入口
  • 傳統開發模式中的多頁面,此時以多元件的方式實現。不嚴謹的說,就是傳統模式下的一個頁面對應這裡的一個元件,當然我們這裡元件的粒度更精細
  • 通過位址列中的 hash 值的變化,尋找對應的元件在頁面中顯示

我們都知道,頁面實際執行中,其實就是將 vue 範例中 render 屬性指定的元件內容替換掉 index.html 中的 id=app 的元素

我們新建一個根元件,將根元件的內容顯示到 index.html 中的 id=app 的元素上

當元件發生切換時,改變根元件中的內容,也就是將其他元件的內容顯示到跟元件上,而根元件又渲染到 index.html 中,所以最終 html 頁面也會變化

引入 vue 的官方路由 vue-router,來實現這一點

 

 

安裝vue-router

npm install vue-router

 

建立根元件

src 目錄下建立 App.vue

<template>
  <div id="app">   
    <!-- 路由出口 -->
    <!-- 路由匹配到的元件將渲染在這裡 -->
    <router-view></router-view>
  </div>
</template>
<script>
export default {
 
}
</script>
<style scoped>
</style>

其中 router-view 作為匹配到路由規則的元件的展示區域

修改打包入口檔案

main,js 中引入 vue-router,並編寫路由規則,並向 vue 範例註冊 router 範例

還要注意,上面 render 函數中將元件名稱改成了 App 元件

重新打包

通過修改位址列中的 hash 值,可以看到對應的元件內容

 

 

2.8 公共元件如何處理

我們不能要求使用者通過修改位址列來切換不同的元件

必須要提供一個導航

而且這個導航,無論 Home 還是 About 元件都需要

所以不能分別在兩個元件中定義一份,否則隨著專案的擴大,維護成本會越來越高

解決方案:提取一個公共導航

src 目錄下新建 NavBar.vue

<template>
  <nav>
    <ul>
      <li><router-link to="/home">首頁</router-link></li>
      <li><router-link to="/about">關於我們</router-link></li>
    </ul>
  </nav>
</template>
<script>
export default {

}
</script>
<style scoped>
ul {
  list-style-type: none;
  overflow: hidden;
}
li {
  float: left;
  padding: 10px;
}
a {
  text-decoration: none;
}
</style>

修改 App.vue,引入上面的元件,並在 router-view 上面使用

重新打包,然後執行 index.html

 

 

現在我們可以通過點選連線切換元件了

至此,一個簡單的 SPA 應用就完成了

2.9 專案目錄調整

隨著專案變大,元件會越來越多,全部都放在 src 目錄下,不便於管理

另外,隨著元件的增多,路由規則也會越來越多,我們當前將所有路由規則寫在 入口檔案 main.js 中,會讓檔案越來越臃腫,另外入口檔案的作用應該是告訴webpack 哪些檔案應該打包,而不應該放路由處理這種邏輯程式碼,所以應該也提取出來

具體操作

  • 新建 views 目錄,存放頁面級元件,如 Home.vue,About.vue
  • 新建 components 目錄,存放區域性元件,如 NavBar.vue
  • 新建 router 目錄,其下新建 index.js,將路由處理相關程式碼放在這裡

router/index.js 中程式碼如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

// 安裝路由功能:
Vue.use(VueRouter)
/**
 * 建立路由規則
 * 當使用者請求不同的hash值時,顯示不同的元件(將不同的元件內容顯示在 <router-view></router-view>)
 */
const routes = [
  { path: '/home', component: Home },
  { path: '/about', component: About }
]

// 建立 router 範例,然後傳 `routes` 設定
const router = new VueRouter({
  routes
})
export default router

 

main.js 中程式碼修改如下

import Vue from 'vue'
import router from './router' // 可以省略 index.js
import App from './App.vue'

let vm = new Vue({
  el: '#app', // 指定當前vue範例的掛載點,也就是將index.html 中的id=app 的dom作為掛載點
  // 將 Home 元件中的模板和樣式渲染到 index.html 頁面中,具體來說,
  // 就是用模板中的內容替換 index.html 中的 id=app 的元素,也會將 css 放到頁面中
  render: h => h(App),
  router
})

執行命令,重新打包,發現程式能夠正常執行

npm run dev

3. 總結

經過一系列的步驟,我們手動建立的第一個單頁面應用已經成功

仔細對比一下,發現與使用 vue 腳手架建立專案,目錄結構基本一致了