先看一下产品需求,以下图所示,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>