使用 Vue-cli 创建完项目,还需要进行一些配置才能进入开发模式,如果有老大当然是老大来做这些,但是万一这是落到你头上,可不懵逼了,简单的我总结下我配置的流程。
一、初始化样式
在 Jquery 的网页时代我们写项目的时候,为了去除 HTML 默认标签的影响,最常用的是雅虎的 reset.css 当然你也会参考别人自己写的。目前比较全的CSS重设(reset)方法总结
转眼到了 Vue 横行的 MVVM 时代。
Vue 项目的 reset.css或common.css
引入这个东西主要是解决两个问题:
- 隐藏浏览器的侧边滚动条,避免对布局的影响。
- 去除 HTML 标签的默认样式。
就算您不看下面的代码,在写项目的时候,也得把下面代码用到你的项目中。一般讲引用的位置有两个可以选择的地方:
- 第一个地方:main.js 文件(推荐)
import '../public/css/reset.css';
- 第二个地方在 App.vue 中引用,同时记住我们在引入的时候去掉 scoped
<template>
<div id="app" class="fillcontain">
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less">
@import './style/common';
</style>
针对上面的样式文件做几点解释:
body, div, span, header, footer, nav, section, aside, article, ul, dl, dt, dd, li, a, p, h1, h2, h3, h4,h5, h6, i, b, textarea, button, input, select, figure, figcaption {
padding: 0;
margin: 0;
list-style: none;
font-style: normal;
text-decoration: none;
border: none;
font-family: "Microsoft Yahei",sans-serif;
-webkit-tap-highlight-color:transparent;
-webkit-font-smoothing: antialiased;
&:focus {
outline: none;
}
}
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar
{
width: 0px;
height: 0px;
background-color: #F5F5F5;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track
{
-webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0);
border-radius: 10px;
background-color: #F5F5F5;
}
/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb
{
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: #555;
}
input[type="button"], input[type="submit"], input[type="search"], input[type="reset"] {
-webkit-appearance: none;
}
textarea { -webkit-appearance: none;}
html,body{
height: 100%;
width: 100%;
// background-color: #F5F5F5;
}
.fillcontain{
height: 100%;
width: 100%;
}
.clear:after{
content: '';
display: block;
clear: both;
}
.clear{
zoom:1;
}
.back_img{
background-repeat: no-repeat;
background-size: 100% 100%;
}
.margin{
margin: 0 auto;
}
.left{
float: left;
}
.right{
float: right;
}
.hide{
display: none;
}
.show{
display: block;
}
.ellipsis{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.multiellipsis3{
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
/*height:auto是必需的,否则,如果原始图片有设定height,max-width生效的时候,图片就会被水平压缩。
强制height为auto可以确保宽度不超出的同时使图片保持原来的比例。*/
img{
max-width: 100%;
height: auto !important;
}
-
-webkit-tap-highlight-color:transparent;
这个属性只用于 iOS (iPhone和iPad)。当你点击一个链接或者通过 Javascript 定义的可点击元素的时候,它就会出现一个半透明的灰色背景。把 -webkit-tap-highlight-color 设置为透明,重设它。 -
-webkit-font-smoothing: antialiased;
-webkit-font-smoothing 控制的字体渲染只对MacOS的Webkit有效。具体参考这个博客:CSS 中 -webkit-font-smoothing: antialiased 反而让字体更难看了? -
outline: none;
可以在 pc 端为 a 标签定义这个样式的目的是为了取消ie浏览器下点击a标签时出现的虚线和取消chrome下默认的文本框聚焦样式。
-
::-webkit-scrollbar
页面溢出的滚动条
这个千万记住,一定一定要加上,不然你在布局的时候,就尴尬了。我主要遇见两个问题:
- 页面过宽会跳动,去看看这个页面出现滚动条的时候没有跳动demo感受下导航跳动问题。
- 第二个就是页面导航条也是 body 的一部分,如果去掉滚动条的时候,你页面恰好平铺整个页面,但是滚动条出现可能导致盒子剩余宽度太窄,整个布局盒子掉了下来,在或者滚动条压住了内容。
这个滚动条这把我给折腾的够呛,那就多说几句,不重新写篇关于这个的文章了。还有个常见的问题就是页面水平居中布局的时候,水平居中布局与滚动条跳动的千年难题,这个问题也是常见,挺难弄的。主要待解决的问题是:
当前web届,绝大多数的页面间布局都是水平居中布局,主体定个宽度,然后
margin: 0 auto
的节奏~然而,这种> > 布局有一个存在一个影响用户体验的隐患。应该都知道,现代浏览器滚动条默认是overflow:auto类型的,也就是如> 果尺寸不足一屏,没有滚动条;超出,出现滚动条。于是,问题来了:
- 信息流页面,新出现的页面有可能超宽,出现纵横向滚动条。
- JS交互,本来默认页面高度不足一屏,结果点击了个“加载更多”,内容超过一屏,滚动条出现,页面主体就会左侧跳动。
- 结构类似几个页面通过头部的水平导航刷新切换,结果有的页面有滚动条,有的没有。造成的结果就是,导航尼玛怎么跳来跳去!
張鑫旭推荐的方法:
html {
overflow-y: scroll;
}
:root {
overflow-y: auto;
overflow-x: hidden;
}
:root body {
position: absolute;
}
body {
width: 100vw;
overflow: hidden;
}
居中布局倒是没问题,有问题就是如果内容布满屏幕,同时高度超出屏幕产生了滚动条,就会发现滚动条覆盖了文字。所以单页面应用需要铺满屏幕,避免产生滚动条还是使用::-webkit-scrollbar
。
-
-webkit-appearance: none;
IOS 环境下的按钮都是经过美化的,前端爲了方便會去除這些默認的樣式。
去除完樣式幾乎就變成了這樣:
- html 和 body 百分之百撐滿盒子。
這個沒啥好講的,百分比佈局熟的人都知道。
剩下的就是一些常用的盒子居中,左右浮動,文本溢出隱藏。
Vue項目中的 mixin.css
这个可有可无。
如果你項目中使用了 CSS 預處理器,但是只是用了嵌套語法那可太。。。所以我們不管用不用得到,都應該放一點變量定義,混合等等預處理器的語法,不然你心裏過億的去嗎。姑且我們就叫這個文件為 mixin.css。
@blue: #3190e8;
@bc: #e4e4e4;
@fc:#fff;
// 背景图片地址和大小
.bis(@url) {
background-image: url(@url);
background-repeat: no-repeat;
background-size: 100% 100%;
}
//定位全屏
.allcover{
position:absolute;
top:0;
right:0;
}
//transform上下左右居中
.ctt {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
//定位上下左右居中
.ctp(@width, @height) {
position: absolute;
top: 50%;
left: 50%;
margin-top: -@height/2;
margin-left: -@width/2;
}
//定位上下居中
.tb {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
//定位左右居中
.lr {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
//宽高
.wh(@width, @height){
width: @width;
height: @height;
}
//字体大小、行高、字体
.ft(@size, @line-height) {
font-size: @size;
line-height:@line-height;
}
//字体大小,颜色
.sc(@size, @color){
font-size: @size;
color: @color;
}
//flex 布局和 子元素 对其方式
.fj(@type: space-between){
display: flex;
justify-content: @type;
}
全局的 global.css
这个写项目的时候也是必须的可以放到 reset.css/common.css 里面。主要是写类似后台管理系统进行布局的时候,底部、顶部、侧边栏、能够通过继承有个高度。或者类似下图登录入口就简单是个登录页:
global.css 如下:
html,body,#app{
width: 100%;
height: 100%;
}
二、Vue 中 axios 的封装
这里讲个最简单的:
config文件下
┣✈http.js axios封装
┣✈api.js 接口目录
┣✈config.js 全局配置
首先看全局的配置文件 config.js 文件:
// 不同环境的接口
const proURL = 'http://127.0.0.1:7777/api';
const devURL = 'http://127.0.0.1:7777/api';
// 不同的环境ajax请求前缀设置
const baseURL = process.env.NODE_ENV === 'production'? proURL : devURL;
const config = {
//baseURL
baseURL: baseURL,
// 设置超时时间
timeout: 50000,
// 返回数据类型
responseType: 'json', // default
// 请求的接口,在请求的时候,如axios.get(url,config);这里的url会覆盖掉config中的url
url: '/',
// 请求方法同上
method: 'get', // default
transformRequest: [function (data) {
// 这里可以在发送请求之前对请求数据做处理,比如form-data格式化等,这里可以使用开头引入的Qs(这个模块在安装axios的时候就已经安装了,不需要另外安装)
// data = axios.stringify(data);
// console.log('data',typeof data,data)
return data
}],
transformResponse: [function (data) {
// 这里提前处理返回的数据
if (typeof data === 'string') {
data = JSON.parse(data)
}
return data
}],
// 请求头信息
headers: {
//'Content-Type': 'application/x-www-form-urlencoded',
//'Token': '',
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
// parameter参数
params: {},
// post参数,使用axios.post(url,{},data);
data: {},
withCredentials: true
// 当我们把此配置项设置成true的时候,可跨域携带cookie。
}
export default config;
再来看看 http.js 和拦截器:
/**
* http配置
*/
import axios from 'axios';
import config from './config';
import store from '../store';
// 引入axios以及element ui中的loading和message组件
import { Loading , Message} from 'element-ui';
const instance = axios.create(config);
const loadingInstance;
// http请求拦截器
instance.interceptors.request.use(config => {
//写入header token
if (localStorage.getItem('token')) {
config.headers.Authorization = localStorage.getItem('token');
}
// element ui全局菊花图 Loading方法
loadingInstance = Loading.service({
lock: true,
text: '数据加载中,请稍后...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
return config;
}, error => {
// 关闭菊花图
loadingInstance.close();
// 提示错误
Message.error('请求错误');
return Promise.reject(error);
});
// http响应拦截器
instance.interceptors.response.use(response => {
// 响应成功关闭loading
loadingInstance.close();
//对响应数据做些事
var token = response.headers.authorization;
if (token) {
//路由跳转判断依据
store.commit("setToken", token);
// 每次请求携带
localStorage.setItem("token", token);
};
if (response.status === 200) {
const data = response.data;
//后台给的过期接口
if (data.errorCode == 10000) {
//处理token过期操作
logoutFun();
};
};
return response;
}, error => {
// 关闭菊花图
loadingInstance.close();
switch (error.response.status) {
case '401':
Message.error('未授权');
window.location.herf="/login";
break;
case '400':
Message.error('参数错误');
break;
case '403':
// token验证失败
Message.error('登录超时,请重新登录!');
break;
case '404':
Message.error('未找到页面!');
break;
case '500':
Message.error('服务器异常!');
break;
case '504':
Message.error('网关错误!');
break;
default:
Message.error('系统发生未知异常,请稍后重试!');
};
return Promise.reject(error);
});
function logoutFun() {
// 清空本地缓存的token和store里的token
store.commit("setToken", "");
localStorage.removeItem("token");
// 跳转至登录页,并携带用户退出时或token失效时的页面路由,方便登录后直接跳转到此页面。
window.location.herf="/login";
}
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function get(url, params = {}){
return new Promise((resolve, reject) =>{
instance.get(url, {
params: params
})
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
};
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function post(url, params = {}) {
return new Promise((resolve, reject) => {
instance.post(url, JSON.stringify(params))
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data);
});
});
};
export function put(url, params = {}) {
return new Promise((resolve, reject) => {
instance.put(url,JSON.stringify(params))
.then(response => {
resolve(response.data);
}).catch(err => {
reject(err.data);
});
});
};
拦截器这里两个作用一个是加载菊花图,另一个是 token登录。loading 图的制作,elementUI 给的 loading 符合单例模式,服务状态下一次只能打开一个,如果你使用 iview 的话,请使用 Vuex 来控制 loading 图的出现和消失。
还有一个 api.js 文件:
//api接口统一管理
export default {
user:{
superUser:'/api/console/public/signin',//超级管理登录
addUser:'/api/console/user',//用户创建接口,
userQuery:'/api/console/user',//用户列表,
groupQuery:'api/console/group/1000/1/0',//组查询接口
},
article:{
...
}
}
依据网页的不同模块,人多了的话,把负责 user 这块的单独拎出来成一个文件。router.js 也可以这么玩。
main.js 中挂载全局:
//axios
import { get, post , put } from './config/http';
import api from './config/api';
Vue.prototype.$get = get;
Vue.prototype.$post = post;
Vue.prototype.$put = put;
Vue.prototype.$api = api;
项目中使用:
this.$get(this.$api.user.superUser).then(res=>{}).catch(err=>{});
一直到今天才对 axios 的封装有点新的。。。
三、關於Vue項目中配置跨域的問題
如果老天開眼後臺給你配置了跨域,那麽我們就不用去 vue.config.js 中去配置代理跨域了,直接在 axios 的 baseURL 中進行全局配置測試接口即可,例如:
baseURL:'http://test.api.lndl-energy.com/
再在 api.js 接口文件裏面定義接口鏈接即可。但是一般來講開發不可能不會跨域的,除非你兩個人都在服務器上開發,還的是統一文件裡面。這當然是不可能的還是的去配跨域。腳手架的跨域在 vue.config.js(和package.json同級) 裡面配置。
vue.config.json:
// const path = require('path');
// function resolve(dir) {
// return path.join(__dirname, dir)
// }
module.exports = {
// baseUrl type:{string} default:'/'
// 将部署应用程序的基本URL
// 默认情况下,Vue CLI假设您的应用程序将部署在域的根目录下。
// https://www.my-app.com/。如果应用程序部署在子路径上,则需要使用此选项指定子路径。例如,如果您的应用程序部署在https://www.foobar.com/my-app/,集baseUrl到'/my-app/'.
publicPath: '/',
// outputDir: 在npm run build时 生成文件的目录 type:string, default:'dist'
// outputDir: 'dist',
// pages:{ type:Object,Default:undfind }
/*
构建多页面模式的应用程序.每个“页面”都应该有一个相应的JavaScript条目文件。该值应该是一
个对象,其中键是条目的名称,而该值要么是指定其条目、模板和文件名的对象,要么是指定其条目
的字符串,
注意:请保证pages里配置的路径和文件名 在你的文档目录都存在 否则启动服务会报错的
*/
pages: {
index: {
// entry for the page
entry: './src/main.js',
// the source template
template: './public/index.html',
// output as dist/index.html
filename: 'index.html'
}
},
// when using the entry-only string format,
// template is inferred to be `public/subpage.html`
// and falls back to `public/index.html` if not found.
// Output filename is inferred to be `subpage.html`.
// subpage: 'src/subpage/main.js'
// },
//lintOnSave:{ type:Boolean default:true } 问你是否使用eslint
lintOnSave: false,
// productionSourceMap:{ type:Bollean,default:true } 生产源映射
// 如果您不需要生产时的源映射,那么将此设置为false可以加速生产构建
productionSourceMap: false,
// webpack 配置~
// chainWebpack: () => {},
// configureWebpack: () => {
// if (process.env.NODE_ENV === 'production') {
// // mutate config for production...
// } else {
// // mutate for development...
// }
// },
// vue-loader 配置
// vueLoader: {},
css: {
// // 是否提取css生成单独的文件 默认 true
// extract: true,
// // 使用 CSS source maps?
// sourceMap: false,
// loader配置
loaderOptions: {
// 向预处理器 Loader 传递配置选项
scss: { // 配置scss(其他样式解析用法一致)
javascriptEnabled: true // 设置为true
}
},
// // 使用 css Modules
// modules: false
},
// devServer:{type:Object}它支持webPack-dev-server的所有选项
devServer: {
port: 8888, // 端口号
host: 'localhost', //host: "0.0.0.0",如果是真机测试,就使用这个IP
https: false, // https:{type:Boolean}
open: true, //配置自动启动浏览器
hotOnly: false, //热更新(webpack已实现了,这里false即可)
//disableHostCheck: true,
proxy: {
//配置跨域
'/api': {
target: "http://test.api.lndl-energy.com/wallet",
ws:true,
changOrigin:true,
pathRewrite:{
'^/api':'/'
}
}
}
}
};
待完善 vue-cli环境变量
四、elementUI
- elementUI 组件的名可以当类名使用
- 使用elementUI 组件在控制台审查元素出现的类名,因为编辑代码里面没有,所以在编辑器里面必须使用深度选择器穿透来改变样式,使用 /deep/ 。
- <el-container>:外层容器。当子元素中包含 <el-header> 或 <el-footer> 时,全部子元素会垂直上下排列,否则会水平左右排列。
aslid,v-for权限管理一级二级菜单
elementUi自定义字体图标库,icon asset font,通过for循环的ID来渲染font类名
unique-opened :unique-opened="true" 选择框之允许打开一列
.el-menu{border-right:1px solid red;}展开多一像素
el-menu collapse 是否折叠,折叠面板变小。el-aside:width="collapse===false?64px;200px;"
el-menu 开启路由调转 router,以#为根开始跳转,default-active=index激活状态,使用session保存,created创建
<el-table-column type="index" width="50">索引列
router-view中使用el-card作为背景
el-row 和el-col,el-input使用span解决,gutter解决中间的间隙