Vue项目中最新用到的一些实用小技巧
写在前面
在最近的 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" alt=""> </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: "#545c64", 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:最终还是引入了一个插件,哈哈????)。
最后叨叨
本文是我最近用到的一些小技巧,如果道友们有更好的实现方法,欢迎在评论区留言讨论,文中错误也欢迎指出,共同学习
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。