vue的 v-for 循環中圖片加載路徑問題

  先看一下產品需求,以下圖所示,css

  產品要求圖片和它的名稱一一對應,原本是很是簡單的需求,後臺直接返回圖片路徑和名稱,前臺直接讀取就能夠了,可是咱們沒有存儲圖片的服務器,再加上是一個實驗性的需求,圖片須要存放到前臺。當時我想,vue 中的img 的src 能夠動態綁定到一個變量上, 很簡單嗎,就沒有考慮太多,直接開始作了。html

  首先和後臺商量一下數據結構,由於圖片要和名稱一一對應,因此後臺要返回中英文的名稱的映射,我把前臺的圖片名稱直接設置給後臺給的英文名稱,從而讀取圖片,圖片和中文名稱就一一對應了。數據結構以下:映射關係用對象表示,多個圖片,因此放到一個數組中前端

[
            {
                CnName:'荷花',
                EnName: 'lotus'
            },
            {
                CnName:'康乃馨',
                EnName: 'carnations'
            },
            {
                CnName:'牡丹',
                EnName: 'peony'
            }
        ]

  如今前臺用vue-cli, 後臺用express 來模擬一下當時的開發場景,也能夠還原一下錯誤和業務的迭代過程。新建一個文件夾,就叫vue-img吧,而後再在該文件夾中新建兩個文件夾,client, server, client 表示客戶端代碼,server 表示服務端代碼。 在client 文件夾中,打開命令窗口,執行 vue init webpack-simple . 命令,後面的點表示當前文件夾,爲了簡單,這裏使用了simple 模版. server 文件夾,打開命令窗口,先執行npm init 初始化爲node 項目,而後npm install express cors --save, 安裝依賴,cors 是解決跨域的。vue

  先來寫前端代碼,把app.vue 中的template和script中的內容清空,保留它的css 樣式內容,咱們就不用寫樣式了。前端頁面,有兩個部分,一個是button, 點擊按鈕來發送請求,一個是圖片展現區域,它用的就是v-for  循環, template 內容以下node

<div id="app">
  <button @click='getFlower'>點擊加載請求</button>
  <!-- 因爲當時想固然地覺得,src 就是綁定一個變量,因此就設置了一個默認變量,這是出錯的過程 -->
  <ul>
      <li v-for ="item in flowers" :key="item">
          <img :src="defaultImg" alt="flowers">
          <p>{{item}}</p>
      </li>
  </ul>
</div>
</template>

  因爲template中用到了方法 getFlower,  變量defaultImg 和 flowers, 因此要在script中進行聲明。defaultImg 是一個圖片,因此還要引入進來, 在src  目錄中新建一個img文件夾,放幾張圖片。flowers是一個數組,我先預寫了一個['荷花', '康乃馨'],getFlower,由於沒有後臺,因此先沒有寫, 注意若是數據量大的,交互複雜,是要寫mock 數據的,這裏比較簡單就沒有寫。這也是出錯的緣由。代碼以下webpack

import defaultImg from './img/lotus.jpg'; // import 引入圖片

export default {
  data() {
    return {
      flowers: ['荷花', '康乃馨'],
      defaultImg: defaultImg
    };
  },
  methods: {
    getFlower() {}
  }
};

  整個頁面顯示以下,我覺得沒有問題了。ios

  如今再來寫後臺代碼, 用express 寫一個後臺接口,仍是很是簡單的。在server 文件夾中,建一個文件server.js 來寫後臺代碼web

var express = require('express');
var cors = require('cors');  // 引入cors 中間件,解決跨域

var app = express();
app.use(cors());

// 前端發送的是get請求,接口是flowers. 返回的數據code 表示成功或失敗,obj 表示數據
// 數據中Cn 表示中文名稱,En表示中文名稱
app.get('/flowers', (req, res) => {
    res.json({
        code: 0,
        obj: [
            {
                CnName:'荷花',
                EnName: 'lotus'
            },
            {
                CnName:'康乃馨',
                EnName: 'carnations'
            },
            {
                CnName:'牡丹',
                EnName: 'peony'
            }
        ]
    })
})
app.listen(3000, () => {
    console.log('server start at 3000');
})

  如今用nodemon server.js 啓動服務,在瀏覽器地址輸入http://localhost:3000/flowers, 能夠看到返回的數據表示接口ok.vue-cli

  如今再從新寫一下前端代碼,進行先後端聯調。因爲要發送請求,還要安裝axios 依賴。首先要根據後臺接口改一下template 內容的li express

  <ul>
      <li v-for ="item in flowers" :key="item.EnName">
          <img :src="item.EnName" alt="flowers">
          <p>{{item.CnName}}</p>
      </li>
  </ul>

  而後,在script中引入 axios, flowers 數組清空,default img 刪除 , 引入後臺數據所須要的三張圖片, 同時getFlower 方法發送請求

// 引入axios,用於發送請求,defaults 設置後臺請求地址
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000"

// 引入相關的圖片, 命名要和後臺保持一致
import lotus from './img/lotus.jpg';
import carnations from './img/carnations.jpg';
import peony from './img/peony.jpg';

export default {
  data() {
    return {
      flowers: [], 
      lotus,
      carnations,
      peony
    };
  },
  methods: {
    getFlower() {
      axios.get('/flowers')
        .then(res => {
            if(res.status === 200 && res.data.code === 0) {
              this.flowers = res.data.obj;
            }
        })
    }
  }
};

  我覺得成功了,點擊按鈕發送請求,可是看到的以下畫面,沒有圖片

 

  當時想不通,img 的src 綁定的是變量,它和defaultImg 不該該是同樣嗎?打開瀏覽器控制檯,看到以下內容,img 的src 已是一個字符串,它不是咱們想要的變量了。

我想這裏多是它對item.EnName進行了一次解析變成了字符串,就完事了,綁定變量,就是解析一次。而對於defalutImg 來講,它原本就是變量,沒法再進行分割解析,因此它會去data 裏面去找,若是找不到,才報錯。

  那麼咱們如今要作的就是把item.EnName 變成圖片的地址,這樣進行一次解析的時候,直接去讀取圖片。要怎麼作到呢?當時 我也不是很清楚,就百度了一下,有人提到了require 方法, require 一個圖片路徑,我想require 什麼,之前沒有據說過require 這個關鍵字啊。想了一段時間,忽然就明白了,require 是一個commonJs 規範的關鍵字,當咱們在寫node 代碼的時候,都是有require 去讀取資源的。在前端js 中,一直使用import,直接忘記了,不知道怎麼用了。webpack 把img 當作一種資源,因此使用時要先引進。引進方式有兩種,一種是import , 一種是require, 由於webpack 同時支持ES6 module 和 commonJs 規範.  import 是個語句,只能在js 代碼頂部使用, 而require 不同,它是一個表達式,能夠進行賦值操做。咱們試一下,用require 引入圖片是怎麼樣的效果,在 script 標籤時,寫下

var img = require('./img/lotus.jpg');
console.log(img);

  刷新瀏覽器,在控制檯上能夠看到以下輸出

  正好是圖片的路徑,也正是咱們想要的內容,剛纔也說了,require是一個表達式,它能夠用到任何js 表達式能用到的地方。我這時就想,把後臺返回的代碼進行從新組裝,EnName 直接是圖片路徑。getFlower 方法修改以下

    getFlower() {
      axios.get('/flowers')
        .then(res => {
            if(res.status === 200 && res.data.code === 0) {
              this.flowers = res.data.obj.map(item => {
                  return {
                    CnName: item.CnName,
                    EnName: require(`./img/${item.EnName}.jpg`)  // 利用require 引入圖片,得到圖片路徑
                  }
                })
            }
        })
    }

  這時刷新瀏覽器,點擊按鈕發送請求,能夠看到圖片了而且一一對應, 成功了。

   

  這時又一想,既然require 是一個表達式,在template模版中是直接能夠解析js 表達式,那麼直接把img 的src 綁定到require 表達式就能夠了,把getFlower 方法,回退到上一次代碼,而後template 代碼以下

  <ul>
      <li v-for ="item in flowers" :key="item.EnName">
          <img :src="require(`./img/${item.EnName}.jpg`)" alt="flowers">
          <p>{{item.CnName}}</p>
      </li>
  </ul>

  一樣也成功了。

  最後寫代碼的時候發現,若是讀取的圖片不存在,上面的方法就會報錯,而且沒有辦法處理。這時還要回到js 的代碼處理。我又把html代碼回到之前,而後在getFlower方法中進行錯誤處理,既然讀取報錯,咱們讀取的代碼就放到try中, 若是有報錯,就在catch 看處理,提供一個默認圖片,try catch 處理讀取異常。 try catch 的邏輯

                  try {
                    img = require(`./img/${item.EnName}.jpg`);
                  } catch (err) {
                    img = require('./img/lotus.jpg');
                  }

  整 個app.vue 

<template>
<div id="app">
  <button @click='getFlower'>點擊加載請求</button>
  <ul>
      <li v-for ="item in flowers" :key="item.EnName">
          <img :src="item.EnName" alt="flowers">
          <p>{{item.CnName}}</p>
      </li>
  </ul>
</div>
</template>

<script>
// 引入axios,用於發送請求,defaults 設置後臺請求地址
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000";

export default {
  data() {
    return {
      flowers: []
    };
  },
  methods: {
    getFlower() {
      axios.get('/flowers')
        .then(res => {
            if(res.status === 200 && res.data.code === 0) {
              this.flowers = res.data.obj.map(item => {
                  var img = null;
                  try {
                    img = require(`./img/${item.EnName}.jpg`);
                  } catch (err) {
                    img = require('./img/lotus.jpg');
                  }

                  return {
                    CnName: item.CnName,
                    EnName: img
                  }
                })
            }
        })
    }
  }
};
</script>
相關文章
相關標籤/搜索