视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Vue项目中最新用到的一些实用小技巧
2020-11-27 22:04:14 责编:小采
文档


写在前面

在最近的 Vue 项目中,为了完成需求使用了一些小技巧,做个笔记,或许也能帮到道友。

阅读重点

需求一:为路径配置别名

在开发过程中,我们经常需要引入各种文件,如图片、CSS、JS等,为了避免写很长的相对路径(../),我们可以为不同的目录配置一个别名。

找到 webpack.base.config.js 中的 resolve 配置项,在其 alias 中增加别名,如下:

创建一个 CSS 文件,随便写点样式:

.avatar
 display: flex;
 justify-content: center;
 align-items: center;

.avatar-img
 padding 20px
 border solid 1px #ccc
 border-radius 5px

接着,在我们需要引入的文件中就可以直接使用了:

<template>
 <div class="avatar">
 <img class="avatar-img" src="~img/avatar.png" ">
 </div>
</template>

<script>
 export default {
 name: "Home"
 }
</script>

<style scoped lang="stylus">
 @import "~css/avatar";
</style>

需要注意的是,如果不是通过 import 引入则需要在别名前加上 ~,效果如下:

需求二:要求实现在生产包中直接修改api地址

这个需求,怎么说呢,反正就是需求,就想办法实现吧。

假设有一个 apiConfig.js 文件,用于对 axios 做一些配置,如下:

import axios from 'axios';

axios.defaults.timeout = 10000;
axios.defaults.retry = 3;
axios.defaults.retryDelay = 2000;
axios.defaults.responseType = 'json';
axios.defaults.withCredentials = true;
axios.defaults.headers.post["Content-type"] = "application/json";

// Add a request interceptor
axios.interceptors.request.use(function (config) {
 // Do something before request is sent
 return config;
}, function (error) {
 // Do something with request error
 return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {
 // Do something with response data
 return response;
}, function (error) {
 // Do something with response error
 return Promise.reject(error);
});

export default axios

在 static 文件夹中增加一个 config.json 文件,用于统一管理所有的 api 地址:

{
 "base": "/api",
 "static": "//static.com/api",
 "news": "//news.com.api"
}

打开 main.js,写入下列代码:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'js/apiConfig'; //import直接引入,不用添加~

Vue.config.productionTip = false;
Vue.use(ElementUI);

/* eslint-disable no-new */
let startApp = function () {
 let randomStamp = new Date().getTime();
 axios.get(`/static/config.json?t=${randomStamp}`).then((data) => {
 axios.defaults.baseURL = data.base; //设置一个默认的根路径
 Vue.prototype.$axios = axios;
 Vue.prototype.$apiURL = data; //将所有路径配置挂载到 Vue 原型上
 /* eslint-disable no-new */
 new Vue({
 el: '#app',
 router,
 components: {App},
 template: '<App/>'
 });
 })
};
startApp();

就是先用 axios 获取 api 文件,然后再初始化。

需求三:由后台根据用户权限值返回菜单

菜单是树形结构(PS:就算不是树形结构,你也得处理成树形结构),我这里使用的是 ElementUI ,参考了道友的这篇文章,实现如下:

新建一个 Menu.vue 文件,写入如下代码:

<script>
 export default {
 name: "MenuItem",
 props: {
 data: {
 type: Array
 },
 collapse: {
 type: Boolean
 }
 },
 methods: {
 //生成菜单项
 createMenuItem(data, createElement) {
 return data.map(item => {
 if (item.children && item.children.length) {
 return createElement('el-submenu', {props: {index: item.id.toString()}},
 [
 createElement('template', {slot: 'title'}, [
 createElement('i', {class: item.icon}),
 createElement('span', [item.title]),
 ]
 ),
 this.createMenuItem(item.children, createElement) //递归
 ]
 )
 } else {
 return createElement('el-menu-item', {props: {index: item.path}},
 [
 createElement('i', {class: item.icon}),
 createElement('span', {slot: 'title'}, [item.title]),
 ]
 )
 }
 })
 },
 //选中菜单
 onSelect(key, keyPath) {
 console.log(key, keyPath);
 }
 },
 render(createElement) {
 return createElement(
 'el-menu',
 {
 props: {
 backgroundColor: "#545c",
 textColor: "#fff",
 activeTextColor: "#ffd04b",
 collapse: this.collapse,
 router:true
 },
 class:'el-menu-vertical-demo',
 on: {
 select: this.onSelect
 }
 },
 this.createMenuItem(this.data, createElement)
 )
 }
 }
</script>

<style scoped lang="stylus">
 .el-menu-vertical-demo:not(.el-menu--collapse) {
 width: 200px;
 min-height: 400px;
 }
</style>

这里主要用到两个东西,一个是 render 函数,一个是递归,如果不熟悉 render 函数的道友请点这里。可能有道友会问为什么不用模板,因为······做不到啊😭,在 template 中只能有一个根元素,而 Vue 了不能对根元素使用 v-for;再者,通过在浏览器中查看代码可以知道,菜单就是 ul 加上 li,如果有了根元素会破坏标签结构(虽然不影响功能,但还是觉得不舒服😂)。然后,在需要使用的地方:

<template>
 <el-container>
 <el-aside width="auto">
 <Menu :data="menu" :collapse="isCollapsed"></Menu>
 </el-aside>
 <el-container>
 <el-header>
 <el-button type="text" icon="el-icon-d-arrow-left"
 @click="isCollapsed=!isCollapsed"></el-button>
 <h3>MenuName</h3>
 <span>MeFelixWang</span>
 </el-header>
 <el-main>
 <router-view></router-view>
 </el-main>
 </el-container>
 </el-container>
</template>

<script>
 import Menu from '@/components/Menu';

 export default {
 name: 'App',
 data() {
 return {
 menu: [
 {
 title: '导航一',
 id: 1,
 path: '',
 icon: 'el-icon-search',
 children: [
 {
 title: '导航一杠一', id: 2, path: '', icon: '', children: [
 {title: '导航一杠一杠一', id: 4, path: '/test', icon: '', children: []},
 {
 title: '导航一杠一杠二', id: 5, path: '', icon: '', children: [
 {title: '导航一杠一杠二杠一', id: 6, path: '/6', icon: '', children: []},
 {title: '导航一杠一杠二杠二', id: 7, path: '/7', icon: '', children: []},
 ]
 },
 ]
 },
 {title: '导航一杠二', id: 3, path: '/3', icon: '', children: []}
 ]
 },
 {title: '导航二', id: 8, path: '/8', icon: 'el-icon-setting', children: []},
 {title: '导航三', id: 9, path: '/9', icon: 'el-icon-document', children: []},
 {
 title: '导航四', id: 10, path: '', icon: 'el-icon-date', children: [
 {title: '导航四杠一', id: 11, path: '/11', icon: '', children: []},
 {
 title: '导航四杠二', id: 12, path: '', icon: '', children: [
 {title: '导航四杠二杠一', id: 14, path: '/14', icon: '', children: []}
 ]
 },
 {title: '导航四杠三', id: 13, path: '/13', icon: '', children: []},
 ]
 },
 ],
 isCollapsed: false
 }
 },
 methods: {
 handleOpen(key, keyPath) {
 console.log(key, keyPath);
 },
 handleClose(key, keyPath) {
 console.log(key, keyPath);
 }
 },
 components: {
 Menu
 }
 }
</script>

<style lang="stylus">
 *
 margin 0
 padding 0

 html, body, .el-container, .el-aside
 height 100%

 .el-aside
 background-color rgb(84, 92, 100)

 .el-menu
 border-right solid 1px rgb(84, 92, 100)

 .el-header
 display flex
 justify-content space-between
 align-items center
 background-color aliceblue
 .el-button--text
 color: #606266;
 i
 font-weight bold
</style>

效果如下:

需求四:这个 Select 选项是树形结构,一定得是树形结构

树形结构就树形结构吧,不就是样式嘛,改改应该就可以了。

<template>
 <div>
 <el-select v-model="tree" placeholder="请选择活动区域">
 <el-option v-for="(item,index) in options" :key="index" :label="item.label" :value="item.id"
 :style="{paddingLeft:(item.level*10+20)+'px'}" :class="item.level?'is-sub':''"></el-option>
 </el-select>
 选择的是:{{tree}}
 </div>
</template>

<script>
 export default {
 name: "Home",
 data() {
 return {
 tree: '',
 options: [],
 originData: [
 {
 label: '这是根一', id: 1, children: [
 {label: '这是茎一一', id: 2, children: []},
 {label: '这是茎一二', id: 3, children: []},
 {
 label: '这是茎一三', id: 4, children: [
 {label: '这是叶一三一', id: 6, children: []},
 {label: '这是叶一三二', id: 7, children: []},
 ]
 },
 {label: '这是茎一四', id: 5, children: []},
 ]
 },
 {
 label: '这是根二', id: 8, children: [],
 },
 {
 label: '这是根三', id: 9, children: [
 {label: '这是茎三一', id: 10, children: []},
 {
 label: '这是茎三二', id: 11, children: [
 {label: '这是叶三二一', id: 12, children: []}
 ]
 },
 ],
 },
 ]
 }
 },
 created() {
 this.options = this.decomposeTree(this.originData, 0);
 },
 methods: {
 //分解树形结构
 decomposeTree(array, level) {
 let tmpArr = [];

 (function decompose(arr, lev) {
 for (let i = 0; i < arr.length; i++) {
 let tmpObj = {};
 let item = arr[i];
 item.level = lev;
 tmpObj = Object.assign({}, item);
 tmpArr.push(tmpObj);
 if (item.children) {
 decompose(item.children, lev + 1); //递归
 }
 delete tmpObj.children; //删掉其 children,避免数据过大(不删也可以,也许后面有用呢)
 }

 })(array, level);

 return tmpArr;
 }
 }
 }
</script>

<style scoped lang="stylus">
 .is-sub:before
 content '- '
</style>

因为 option 接收的是一个一维数组,所以通过递归展平树形结构,在展平的时候设置每项的层级,通过层级来设置缩进及前缀符号,效果如下:

之所以这样做,是因为是管理系统,简单有效,没必要因为这一个组件引个新的插件或者自己写一个(以后用得着的除外哈);也可以用 input 加上 tree 控件来模拟(PS:最终还是引入了一个插件,哈哈😂)。

最后叨叨
本文是我最近用到的一些小技巧,如果道友们有更好的实现方法,欢迎在评论区留言讨论,文中错误也欢迎指出,共同学习

下载本文
显示全文
专题