书接上文
上一篇粗略地搭建了项目,运行一下,编译通过,但是控制台上打出一个红色错误警告!报错了?!什么情况,还没做事就错了,来看看什么错!
'Locale' is defined but never used,就是说Locale这个变量没有使用。在引入UI组件库的时候,我的编辑器已经错误提示了,莫名其妙嘛。并非这个错抛得莫名其妙,而是这个错不是自己造成的。设想一下,在插件的基础上加了很复杂的逻辑,这时来一个这样莫名其妙的错,是不是很无语。这只是一个假想,绝大多数情况不会是插件带来的。不过,我还是选择自己撸代码,不去使用所谓的插件,除非使用例如Babel之类的工具。
除此之外,使用ESLint还会有更多的错误提示。比如,配置postcss.config.js的时候也会给出错误警告,我在上一篇文章给的配置文件的key使用的是双引号,它会提示使用单引号。再比如,ES推荐使用箭头函数,在某些时候没用使用箭头函数也会跑错,特别是使用插件。代码提示会抛出很多语法上的错误提示,如果开了ESLint,那就按着它的提示来培养代码编写习惯吧。
// 重新给一下postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
'postcss-url': {},
'postcss-aspect-ratio-mini': {},
'postcss-write-svg': {
uft8: false
},
'postcss-cssnext': {},
'postcss-px-to-viewport': {
viewportWidth: 375,
unitPrecision: 3,
viewportUnit: 'vw',
selectorBlackList: ['.ignore', '.hairlines'],
minPixelValue: 1,
mediaQuery: false
},
'postcss-viewport-units': {
'silence': true
},
'cssnano': {
preset: 'advanced',
autoprefixer: false,
'postcss-zindex': false
}
}
}
我不喜欢ESLint之类的语法错误提示,我记得最搞笑的一次,我的编辑器设置的4个空格格式化代码。当时使用的webstorm编辑器,它可以给开发者提供了vue模版,然后生成的代码也是4个空格的规则。可是当我运行代码的时候就抛错了,它要求使用的2个空格,排查了很久才发现。我就在想,如果说类似变量声明没有使用之类的错误抛出是可以接受的,像这2个空格之类的抛错真的有必要吗?当然,这类工具可以自己配置,只是感觉默认值很瓜。
看下页面,先看下PC下的显示,所有元素的间距大小都很大。切换到iphone 6的环境下,这时就是正常的间距。上面配置的375一倍像素,这时默认的设计。vw是根据浏览器视口宽度进行计算的单位,在pc环境下,浏览器视口变大所有元素也会随着发生变化。LOGO没有设置大小,所以一直保持这个尺寸。
试着改变浏览器大小,这时这些元素也会随之变化,这样就无需JS去监听resize事件。这时它的好处,也算得上是缺点:它是根据视窗发生变化的,所以移动端和PC端必须分开。使用rem可以设置一个峰值,当监听到视窗宽度达到峰值就按一倍像素展示不再缩放,vw则不能。
小到iPhone 5,大到iPad,效果都还不错,可以下一步了。
网络请求
## 拦截封装 ##
网络请求是一个必然,我准备使用axios,这个库使用的ES6语法,就很棒。
# 安装axios
npm i -D axios
在src下新建一个apis的目录,做一个简单的拦截封装。这个封装我在别的项目用过,数据请求都没问题,由于这个项目缺少后端接口的环节,因此暂且只是一个普通的封装。
// 封装拦截 http.js
import axios from 'axios'
// 默认参数根据情况配置
// 接口地址环境变量 process.env.VUE_APP_HOST
axios.defaults.baseURL = process.env.VUE_APP_HOST
axios.defaults.timeout = 10000
axios.defaults['Content-type'] = 'application/json;charset=UTF-8'
axios.interceptors.request.use(config => {
// 参数处理
return config
}, error => {
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
// 响应处理
return response
}, error => {
// 错误处理
return Promise.reject(error)
})
// 请求封装
export function request(url, method = 'get', params = {}) {
params = /(get)|(delete)|(head)/ig.test(method) ? { params } : params
return axios[method](url, params)
}
export default axios
// 具体接口文件 api.js
// 可以根据业务需求分模块编写,按需引入
import { request } from './http'
export const getImgCode = params => request('/utils/public/yzm/get', 'post', params)
按照我的做法,在http.js拦截的时候会统一进行错误处理。为了防止多次点击按钮触发请求,会在请求开始使用模态loading防止多次触发请求。我并没有完全拦截错误,封装保持错误的返回,因为有时需要在错误里做后续操作。Promise在catch之后再将同一个错误抛出,在最后还需要catch一次。
暂且做这样的封装,在开始做接口之后,这个封装会改为async/await语法来封装,整个封装结构再重新考虑,这是后话。
## 跨域请求 ##
接口开发和前端开发所在的网络必然会造成跨域问题,这个问题可以通过服务器允许跨域来处理。此外,还可以通过本地的代理来处理这个问题,配置一下vue.config.js。
devServer: {
proxy: {
"/api": {
target: process.env.VUE_APP_PROXY,
changeOrigin: true,
ws: true,
pathRewrite: {
"^/api": ''
}
}
}
}
接口地址被代理,那么http.js中的baseURL对应的地址是/api。通常码好http.js、vue.config.js这类文件,在其他项目中几乎可以直接使用,所以,我将这两个文件对应的地址改为了环境变量process.env.VUE_APP_HOST、process.env.VUE_APP_PROXY,以后只需修改环境变量就可以了。
在项目根目录,新建三个文件,它们分别代表三个环境,development、production是vue默认的开发环境和生产环境。事实上,至少还需要一个环境,测试版的生产环境。总不可能在本地开发完了就直接打包进生产环境,于是需要先打包上测试服务器,测试完成再部署生产环境。这个环境我用的debug来标识,三个文件分别是:.env.development、.env.production和.env.debug。值得一提的是:这个文件是按文本读取,它的值是字符串类型。
NODE_ENV=development
VUE_APP_HOST=/api
NODE_ENV=production
VUE_APP_HOST=https://www.production.com
NODE_ENV=production
VUE_APP_HOST=https://www.debug.com
关于环境变量官方文档说得很详细,我就不做赘述。环境变量配置好了,给测试服务器打包,还需要在package.json里面增加一个脚本。因为它和build执行的是同一编译环境,增加一个参数就好了:
"debug": "vue-cli-service build --mode debug"
这个debug环境可以增加一些其他处理,比如打包的时候分析包的占比,这在后面打包的时候会提到。
前期工作
## 样式重置 ##
在开始页面编写之前我们通常需要重置html元素的默认样式。为了统一样式,还需要申明一些变量来进行控制颜色、字体大小等等,这是全局的。更多的样式是精确到组件,但是主体的标准是来自全局,从而形成自己的UI库。除此之外还会有公共样式,比如1px边线的处理,文字溢出处理等等。
在技术选型的时候,我没有提到动态语言的选择,用哪种语言并不影响最后的代码。这部分无需多言,根据自己的喜好进行就好了。
## svg图标 ##
我不主张使用字体图标,这是一个庞大的资源。我在首页引入了vant的tabbar组件,5个图标,有时会出现方格状态(资源未加载完的情况)。字体图标最大的好处,我认为就是它能像字体一样进行大小控制,有得必有失嘛。为了图标在放大缩小的时候不失真,我选择使用字体图标。
作为图标必然会在很多地方使用,所以在components编写一个组件SvgIcon.vue。
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"/>
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
return `svg-icon ${this.className}`
}
}
}
</script>
<style scoped lang="scss">
.svg-icon {
display: block;
min-width: 1em;
fill: currentColor;
overflow: hidden;
}
</style>
这个组件有两个属性:svg的名字及样式名。样式.svg-icon的fill目的是让svg图标颜色与字体颜色一致,属性里的样式名用于组件自定义样式。值得注意的是,svg文件源码写有fill属性且有颜色值无法修改图标颜色,需要去掉这个属性,或者修改fill=“currentColor” ,或者修改fill=""。
在src下新建一个目录icons,再在这个目录下新建一个目录svg和index.js文件。svg目录存放svg文件,index.js用于加载所有的svg。这不是必要的,可以按需引入SvgIcon组件即可。
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
Vue.component('svg-icon', SvgIcon)
const create = ctx => ctx.keys().map(ctx)
create(require.context('./svg', false, /\.svg$/))
在main.js中引入icons。如果package.js中没有配置入口文件,默认是index这个文件,因此直接引入目录icons即可;如果配置有入口文件,那么编译时会先去找这个文件。写全路径本身肯定不会有问题。
import '@/icons'
vue内部原本是有svg这个规则,它不太能满足需求,改用svg-sprite-loader。
# 安装svg-sprite-loader
npm i --save-dev svg-sprite-loader
在vue.config.js中进行相关配置:
// 引入path
const path = require('path')
// 修改chainWebpack
chainWebpack: config => {
// 清除默认svg规则改为svg-sprite-loader
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule.exclude.add(/node_modules/)
svgRule.test(/\.svg$/).use('svg-sprite-loader').loader('svg-sprite-loader').options({
symbolId: 'icon-[name]'
}).end()
// 修改images规则
const imagesRule = config.module.rule('images')
imagesRule.exclude.add(path.join(__dirname, 'src/icons'))
config.module.rule('images').test(/\.(png|jpe?g|gif|svg)(\?.*)?$/).end()
}
配置完成,在页面上引入:
<svg-icon iconClass="history" className="icon"></svg-icon>
最后
截止到目前,准备工作算是完成,可以开始逻辑代码的编写啦。水平有限,能力一般,有什么不对的地方,欢迎大家不吝赐教。
## 代码仓库 ##
https://gitee.com/IanLew/tree-hole.git
## @树洞系列 ##
vue项目实践@树洞(二)