天天看點

【學習筆記】webpack + Vue 元件化開發

文章目錄

    • 安裝
      • 為什麼需要安裝局部webpack
    • 使用
      • 準備工作
      • 基本使用
    • 配置
      • 入口和出口
      • webpack映射
    • loader
      • 打包css檔案
      • 打包scss檔案
      • 打包圖檔資源
        • file-loader
        • 修改檔案名稱
      • ES6 轉 ES5
    • webpack 中的 Vue 使用
      • 編寫Vue代碼
        • 抽取template
        • vue-loader
      • 元件化開發Vue
        • 導入元件省略擴充名
    • webpack中的插件
      • loader和plugin差別
      • plugin的使用過程:
        • 添加版權插件
        • HTML打包插件
        • js壓縮插件
    • 搭建本地伺服器
    • 配置檔案的抽離

安裝

  1. 安裝webpack前要先安裝Node.js
  2. 全局安裝webpack:

    npm install [email protected] -g

    (這裡先指定3.6.0版本,因為vue cli2依賴該版本,可以進行手動配置,便于學習)
  3. 局部安裝webpack:進入到項目根目錄,輸入

    npm install [email protected] --save-dev

為什麼需要安裝局部webpack

因為一個項目往往依賴特定的webpack版本,全局的版本可能很這個項目的webpack版本不一緻,導出打包出現問題

是以通常一個項目,都有自己局部的webpack

執行局部webpack打包:

node_modules/.bin/webpack

使用

準備工作

  1. 建立src檔案,建立兩個互相依賴的js檔案
  2. 建立dist檔案,儲存打包後的檔案
  3. 建立index.html檔案(根目錄下)

基本使用

在終端進入到項目根目錄,輸入指令:

webpack ./src/main.js ./dist/bundle.js

該指令的意思是将main.js在該項目内所依賴的所有js檔案打包到dist目錄下的bundle檔案

如果報錯禁止運作腳本,則需要配置powershell

配置

入口和出口

我們每次使用webpack的指令都需要寫上入口和出口作為參數,非常麻煩,是以我們要建立一個webpack配置檔案:webpack.config.js,在該檔案配制好入口和出口後,下一次隻需輸入

webpack

即可打包

絕對路徑需要依賴node的path包,一般隻要依賴于node環境的項目,都需要建立package.json檔案,該檔案是告訴關于項目一些資訊以及管理node包,使用

npm init

指令建立

如果項目有依賴一些node的包,使用

npm install

會根據package.json自動安裝所需要的包

// 用于擷取絕對路徑,需要依賴node的path包,node全局中是包含該包的,是以不需單獨建包
const path = require('path')
module.exports = {
  // 入口:可以是字元串/數組/對象,這裡我們入口隻有一個,是以寫一個字元串即可
  entry: './src/main.js',
  // 出口:通常是一個對象,裡面至少包含兩個重要屬性:path和filename
  output: {
    //__dirname:擷取目前檔案所在絕對路徑
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}
           

webpack映射

我們在真正的開發中一般使用

npm run build

來建構我們的項目,因為使用

webpack

指令時後面可能會跟很長的内容,我們想讓這個長指令可以由其他指令代替

webpack映射還有一個好處,我們在任何終端執行webpack的時候都會執行全局webpack,但是我們使用映射的指令會優先執行局部webpack(本地),因為package.json中的scripts的腳本在執行時,會先尋找本地的node_modules/.bin路徑中對應的指令,如果沒找到,再去全局中尋找

打開package.json檔案,在scripts對象内添加"build"屬性

"scripts": {
  // 此時執行npm run build指令将轉換為webpack指令
    "build": "webpack"
  },
           

loader

前面示範了如何打包js檔案,但是如果我們想要打包css、vue、ts等檔案時,需要先給webpack擴充對應的loader,這裡以css為例示範

進入https://www.webpackjs.com/loaders/,找到對應的css的loader,按照文檔操作

  1. 安裝
  • css-loader:

    npm install --save-dev css-loader

  • style-loader:

    npm install style-loader --save-dev

  1. 進入webpack.config.js配置檔案,在

    module.exports

    對象中添加

    module

    對象
module: {
    rules: [
      {
        test: /\.css$/,
        // css-loader:隻負責加載,但不負責解析,也不負責與html檔案連接配接
        // style-loader:将子產品的導出作為樣式添加到 DOM 中
        // webpack在使用多個loader時,是從右向左讀取,我們要先加載css,在添加樣式,是以要将css-loader放到右邊
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
           

打包css檔案

安裝并配置完css的loader,我們就可以開始打包了

我們使用webpack打封包件時,隻會打包入口js檔案所依賴的檔案,不被它依賴的檔案不會被打包;是以我們将css等檔案添加為入口js的依賴就能帶着css檔案一起打包了

// 依賴css檔案
require('./css/normal.css')
           

打包scss檔案

如果我們希望在項目中使用scss、less、stylu來寫樣式,webpack是否可以幫助我們處理呢?我們這裡以scss為例,其他也是一樣的

注意:我們使用Live Sass Compiler插件會實時将scss檔案轉換為css檔案,這些css檔案可以作為我們開發時調整樣式使用,在webpack打包時隻打包scss檔案即可

步驟:

  1. 建立一個scss檔案,然後放在css檔案夾中
  2. 在入口js檔案依賴該scss檔案:

    require('./css/special.scss')

  3. 執行指令:

    npm install sass-loader node-sass webpack --save-dev

  4. 進入webpack.config.js檔案,我們在配置css的loader的時候已經添加了module對象,是以直接在module對象的rules數組裡添加一個新的對象元素
  5. 執行

    npm run build

    ,如果報錯getResolve is not a function,則是因為scss安裝的版本過高,需解除安裝重裝其他版本
// 在webpack.config.js中配置loader
{
   test: /\.scss$/,
   use: [{
       loader: "style-loader" // 将 JS 字元串生成為 style 節點
   }, {
       loader: "css-loader" // 将 CSS 轉化成 CommonJS 子產品
   }, {
       loader: "sass-loader" // 将 Sass 編譯成 CSS
   }]
   }
           

打包圖檔資源

我們在images檔案夾加入兩張圖檔:一張小于8kb的圖檔和一張大于8kb的圖檔,待會兒我們會針對這兩張不同大小的圖檔進行不同的處理

我們已經将normal.css檔案作為main.js的依賴了,是以我們在該css檔案中設定背景圖檔也會作為main.js的依賴;但我們需要為圖檔單獨添加loader

步驟:

  1. 在項目根目錄執行指令:

    npm install --save-dev url-loader

  2. 進入webpack.config.js檔案,在已經建立好的module對象裡的rules數組添加一個新的對象元素
{
  // 設定比對的圖檔格式
  test: /\.(png|jpg|gif)$/,
  use: [
    {
      loader: 'url-loader',
      options: {
        // 限制圖檔大小,圖檔超出limit會用file-loader來打包,需安裝file-loader
        // 圖檔小于limit時,會将圖檔url位址編譯成base64格式字元串
        limit: 8192
      }
    }
  ]
}
           

file-loader

當我們使用的圖檔超出了url-loader配置的大小範圍,則不會再以url-loader來進行加載,而是以file-loader來加載

如果沒有安裝file-loader則會報錯

  1. 安裝file-loader:

    npm install --save-dev file-loader

    ,無需對file-loader專門進行配置

當我們使用file-loader加載圖檔後,發現在dist檔案夾下生成了一個以hash值命名的圖檔,也就是我們的源圖檔;但在網頁中該圖檔并沒有顯示,因為webpack打包後的檔案url路徑預設是根目錄,而不是dist目錄,是以需要修改預設url路徑

  1. 進入webpack.config.js檔案,在output對象中添加

    publicPath

    屬性,以後任何涉及到url的路徑,都會在前面拼接一個

    dist/

output: {
    publicPath: 'dist/'
  },
           

注意:此時我們的index.html在項目根目錄,是以通路資源的時候需要添加

dist/

路徑;到後期我們也會将index.html打包到dist目錄下,到時候需要将

publicPath

屬性删掉

修改檔案名稱

使用file-loader打封包件時會在dist檔案夾生成一個32位hash值命名的檔案,目的是防止檔案名重複

但是在真實開發中我們希望将所有的圖檔放在dist下的images檔案夾下,附帶圖檔原來的名稱,同時也要防止重複

是以我們需要在webpack.config.js檔案中進行配置:

{
   // 1. 找到url-loader的配置項
   test: /\.(png|jpg|gif)$/,
   use: [{
     loader: 'url-loader',
     options: {
       limit: 10000,
       // 2. 在options中添加name屬性
       name: 'images/[name].[hash:8].[ext]'
     }
   }]
}
           

images/[name].[hash:8].[ext]

:表示生成的檔案會在dist的images檔案夾下并以原來的名字+8位哈希值+原字尾命名,如:

歐陽娜娜.d96873f5.jpg

ES6 轉 ES5

webpack在打包我們的js檔案的時候并沒有完全的将ES6文法轉換為ES5,是以我們需要借助babel來幫我們轉換

步驟:

  1. 安裝babel:

    npm install --save-dev [email protected] babel-core babel-preset-es2015

  2. 進入webpack.config.js檔案,在已經建立好的module對象裡的rules數組添加一個新的對象元素來配置babel-loader
{
   test: /\.js$/,
   // 排除掉node_modules檔案夾
   exclude: /(node_modules|bower_components)/,
   use: {
     loader: 'babel-loader',
     options: {
       presets: ['es2015']
     }
   }
}
           

webpack 中的 Vue 使用

  1. 安裝Vue:

    npm install vue --save

  2. 在main.js檔案中導入Vue:

    import Vue form 'vue'

  • 這裡直接寫

    from vue

    即可,如果不加相對路徑,會預設去node_modules檔案夾中導入,而我們的Vue就安裝在此檔案夾
  • 并且node_modules會執行

    export default Vue

    ,是以我們直接

    import Vue

    即可
  1. 此時我們編寫一段Vue代碼,打包并運作,發現報錯using the runtime-only,我們Vue在構件時預設建構runtime-only版本,該版本不允許出現

    <template>

    ,而我們的

    <div id="app">

    其實就是Vue執行個體的template,是以會報錯
  2. 修改vue建構版本:進入webpack.config.js檔案,在module.exports對象中添加一條屬性:
resolve: {
    alias: {
      // 下次import Vue from 'vue'的時候會來這裡看'vue'是否指向了具體的檔案,這裡指向了vue.esm.js
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
           
  1. 重新打包

編寫Vue代碼

假設我們執行個體化了一個Vue對象:

const app = new Vue({
  el:'#app',
  data:{
    msg:'我是一條msg'
  }
})
           

我們以前想要輸出msg的時候會這樣寫:

<div id="app">
  {{msg}}
</div>
           

在真實開發中我們的

<div id="app"> </div>

裡面不會寫任何内容,我們會寫在Vue對象的

template

屬性中,用反單引号包裹:

new Vue({
  el:'#app',
  template:`
  <div>
    <button @click="click">我是template</button>
    <h2>{{message}}</h2>
  </div>
  `,
  data:{
    message:'我是一條message'
  },
  methods:{
    click() {
      console.log('我被點選了')
    }
  }
})
           

這樣在編譯程式的時候,

template

會找到id為app的DOM元素,并将其覆寫替換為

template

中的代碼

抽取template

前面我們将

<template>

代碼和它所需要的

message

資料和

click()

方法都寫到了Vue對象中,顯得非常備援,是以我們要将它們抽取成一個單獨的元件

// 建立APP元件:
const APP = {
  template:`
  <div>
    <button @click="click">我是template</button>
    <h2>{{message}}</h2>
  </div>
  `,
  data() {
    return {
      message:'我是一條message'
    }
  },
  methods: {
    click() {
      console.log('我被點選了')
    }
  }
} 

// 現在的Vue對象:
new Vue({
  el:'#app',
  template:``,
  data:{
  },
  methods:{
  }
})
           

我們想要使用該元件,則需要在Vue對象中注冊它,然後直接在

template

屬性中使用它即可,因為

template

中的内容會覆寫替換掉id為app的DOM元素

new Vue({
  el:'#app',
  // 将APP元件中的内容覆寫替換id為app的DOM元素,注意元件的單标簽形式 斜杠在右邊
  template:'<APP/>',
  // 注冊APP元件
  components:{
    APP
  }
})
           

雖然我們将該元件抽取了出來,但是main.js作為程式入口,不應該儲存這些元件代碼,而且當元件過多時代碼顯得更加混亂,是以我們要為該元件建立一個單獨的js檔案單獨儲存

在src目錄下建立一個vue檔案夾,再建立一個APP.js檔案,在該js檔案裡面專門書寫該APP元件,并導出該元件供其他檔案引用:

export default {
  template: `
  <div>
  <button>我是template</button>
  <h2>{{message}}</h2>
  </div>
  `,
  data() {
    return {
      message: '給我一個吻'
    }
  }
}
           

導出了該元件,我們就可以在main.js檔案中導入使用了:

import APP from './vue/APP'

// 直接在Vue對象中使用APP元件
new Vue({
  el: '#app',
  // APP元件的内容會替換到id為app的DOM元素上
  template: '<APP/>',
  components: {
    APP
  }
})
           

雖然我們用APP.js單獨儲存該APP元件,但是該元件的模闆和js代碼沒有進行分離,是以用js檔案儲存元件是不合适的,要使用 .vue檔案

我們要在vue檔案夾下建立APP.vue檔案,vue格式檔案包含了三部分:

  • <template>

    :書寫元件标簽
  • <script>

    :書寫js代碼
  • <style>

    :書寫樣式

我們将以前的APP.js檔案中的代碼按照分類放入vue格式的檔案中:

<template>
  <div>
    <button>我是template</button>
    <h2>{{message}}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "給我一個吻"
    };
  },
};
</script>

<style>
</style>
           

是不是感覺條理清晰了很多,而且我們可以為

<template>

内的标簽添加樣式,寫在下面的

<style>

标簽中即可

删除以前導入的js檔案,修改為:

import APP from './vue/APP.vue'

vue-loader

我們webpack加載css、scss都需要下載下傳對應的loader,加載vue也不例外:

  1. 安裝:

    npm install vue-loader vue-template-compiler --save-dev

  2. 配置:進入webpack.config.js檔案,在module對象的rules數組裡添加一個新的對象元素:
{
  test: /\.vue$/,
  use: ['vue-loader']
}
           

此時我們執行webpack打包會報錯,因為我們的vue-loader版本過高,我們可以配置為較低的版本來解決此問題

進入package.json,将

vue-loader

的值改為

^13.0.0

,這個

^

的意思是會将版本修改為大于13.0的版本但小于14.0版本中的一個,然後執行

npm install

會自動安裝該版本

  1. 重新打包

元件化開發Vue

下面我們來感受一下什麼是真正的元件化開發

  1. 我們在vue檔案夾下再建立一個Cpn.vue,在

    <template>

    标簽内書寫一些内容,最後的代碼如下:
<template>
  <div>
    <h2>我是Cpn裡的文字</h2>
    <h2>我是Cpn裡的文字</h2>
    <h2>我是Cpn裡的文字</h2>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>
           
  1. 打開之前的APP.vue檔案,在

    <script>

    标簽中導入剛才建立的Cpn元件:

    import Cpn from './Cpn.vue'

  2. 注冊剛導入的Cpn元件
  3. 在APP的

    <template>

    中使用Cpn元件

此時的APP.vue整體代碼如下:

<template>
  <div>
    <button>我是template</button>
    <h2 class="title">{{message}}</h2>
    <!-- 使用導入的Cpn元件 -->
    <Cpn />
    <Cpn />
  </div>
</template>

<script>
// 導入Cpn元件
import Cpn from "./Cpn.vue";
export default {
  data() {
    return {
      message: "給我一個吻",
    };
  },
  // 注冊一下剛導入的Cpn元件
  components: {
    Cpn,
  },
};
</script>

<style scoped>
.title {
  color: green;
}
</style>
           

以後我們的開發模式都是這樣寫元件的,到時候我們的應用程式就是一個元件樹

以現在的代碼為例,main.js引入了APP元件,APP元件就是根元件,而根元件又引入了Cpn元件,Cpn元件以後又引入了其他元件,每個元件都是一個獨立的檔案

導入元件省略擴充名

進入webpack.config.js檔案,在resolve對象中添加:

// 導入js和vue字尾的檔案時不需在寫擴充名,這兩個必須同時添加
  extensions: ['.vue', '.js']
           

webpack中的插件

webpack中的插件(plugin),就是對webpack現有功能的各種擴充,比如打包優化,檔案壓縮等等

loader和plugin差別

  • loader主要用于轉換某些類型的子產品,它是一個轉換器
  • plugin是插件,它是對webpack本身的擴充,是一個擴充器

plugin的使用過程:

  1. 通過npm安裝需要使用的plugins(某些webpack已經内置的插件不需要安裝)
  2. 在webpack.config.js中的plugins中配置插件

添加版權插件

我們先來使用一個最簡單的插件,為打包的檔案添加版權聲明

該插件名字叫BannerPlugin,屬于webpack自帶的插件

  1. 進入webpack.config.js,由于該插件內建到了webpack包中,是以我們要導入webpack包
  2. 在module.exports對象中添加plugins數組
  3. 配置BannerPlugin
const webpack = require('webpack')

module.exports = {
  ...
  plugins: [
    new webpack.BannerPlugin('最終版權歸我所有~')
  ]
}
           

HTML打包插件

目前,我們的index.html檔案是存放在項目的根目錄下的

在真實釋出項目時,釋出的是dist檔案夾中的内容,是以我們需要将index.html檔案打包到dist檔案夾中,這個時候就可以使用HtmlWebpackPlugin插件

該插件的作用:

  • 自動生成一個index.html檔案(可以指定模闆來生成)
  • 将打包的js檔案,自動通過script标簽插入到body中

使用步驟:

  1. 安裝插件:

    npm install html-webpack-plugin --save-dev

    ,最高版本會報錯,建議安裝3.0版本
  2. 引入插件:在webpack.config.js中輸入:

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

  3. 在plugins數組中添加:

    new HtmlWebpackPlugin()

現在重新打包就能在dist檔案夾生成我們的index.html檔案了,但是還存在兩個問題:

  1. 我們之前學習的時候給file-loader的src路徑添加了

    dist/

    ,現在我們的html檔案已經被打包到該路徑了,是以不再需要
  2. 我們希望自動生成index.html的時候添加一個

    <div id="app"> </div>

    标簽

解決:

  1. 進入webpack.config.js,将

    output

    對象中的

    publicPath: 'dist/'

    屬性删掉
  2. 将根目錄下的index.html檔案作為打包html檔案時的模闆:進入webpack.config.js檔案,在plugins數組中的配置中添加:
new HtmlWebpackPlugin({
      // 以目前配置檔案所在目錄下的index.html為模闆
      template: 'index.html'
    })
           

js壓縮插件

  1. 安裝:

    npm install [email protected] --save-dev

  2. 修改webpack.config.js檔案:
// 引入插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  ...
  plugins:[
    new UglifyJsPlugin()
  ]
}
           

搭建本地伺服器

之前我們每次調試一點代碼都要重新打包,很不友善

webpack提供了一個可選的本地開發伺服器,這個本地伺服器基于node.js搭建,内部使用express架構,可以實作我們想要的讓浏覽器自動重新整理顯示我們修改後的結果

  1. 安裝

    npm install --save-dev [email protected]

  2. 在webpack.config.js檔案中配置devServer

devServer也是作為webpack中的一個選項,選項本身可以配置如下屬性:

  • contentBase

    :為哪一個檔案夾提供本地服務,預設是根檔案夾,我們這裡要填寫./dist
  • port

    :端口号
  • inline

    :頁面實時重新整理
  • historyApiFallback

    :在SPA頁面中,依賴HTML5的history模式
// 在webpack.config.js檔案中配置
module.exports = {
  ...
  devServer: {
    contentBase: './dist',
    inline: true
  }
}
           

如果直接在控制台輸入

webpack-dev-server

來運作的話會執行全局webpack,是以我們可以在package.json的scripts對象中添加腳本:

"dev": "webpack-dev-server --open"

,(添加–open,在開啟服務時會自動打開浏覽器)

這樣我們隻需要在終端輸入

npm run dev

,就可以在本地webpack運作我們的服務了,我們修改src目錄下的檔案代碼時,浏覽器就會自動重新整理

想要終止服務,在終端按下

ctrl+c

選擇退出

總結:我們通過

webpack-dev-server

搭建了一個本地服務,之後我們的測試都是在本地服務中進行測試,等全部測試完畢之後,就可以執行

npm run build

進行真正的打包,再把打包後的dist檔案夾放到伺服器進行部署

配置檔案的抽離

在webpack.config.js中,有一些配置是在開發環境中不需要的,如js壓縮插件;有一些配置是生産環境不需要的,如devserver

我們希望将配置檔案分為三份,分别為公共配置、開發配置、生産配置。在程式開發時,執行公共配置和開發配置;在程式上線後,執行公共配置和生産配置,是以還需要檔案合并工具

步驟:

  1. 安裝檔案合并工具:

    npm install webpack-merge --save-dev

    ,推薦安裝4.0版本,高版本會報錯,參考https://blog.csdn.net/sinat_37503265/article/details/107216055
  2. 在根目錄建立build檔案夾用來儲存配置檔案
  3. 建立base.config.js,dev.config.js,prod.config.js三個配置檔案
  4. 将我們以前的webpack.config.js中的配置項按照分類分别放到這三個檔案中
  5. 合并檔案,這裡以合并公共配置和生産配置為例,打開生産環境配置檔案:
// 導入js壓縮插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 導入webpack-merge插件
const webpackMerge = require('webpack-merge')
// 導入base.config.js
const baseConfig = require('./base.config')
// exports導出的時候,将base.config和prod.config一起導出
module.exports = webpackMerge(baseConfig, {
  // 原來prod.config中的代碼
  plugins: [
    new UglifyJsPlugin()
  ],
})
           
  1. 修改預設配置檔案,修改package.json檔案中的

    build

    dev

    腳本,添加

    --config

    字段
{
  ...
  "scripts": {
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
  },
}
           
  1. 修改webpack打包目錄,進入base.config.js,修改path:
module.exports = {
  output: {
    path: path.resolve(__dirname, '../dist')
  },
}
           

繼續閱讀