написать впереди
В недавнем проекте Vue, чтобы завершить требования к использованию нескольких трюков, обратите внимание, и, возможно, также помогают наркоманам.
Основные моменты чтения
Требование 1. Настройте псевдоним для пути
В процессе разработки нам часто приходится внедрять различные файлы, такие как картинки, 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, а затем инициализируйте его.
Требование 3: Возврат в меню из фона в соответствии со значением разрешения пользователя
Меню представляет собой древовидную структуру (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>
Эффект следующий:
Требование 4: Этот параметр выбора представляет собой древовидную структуру, она должна быть древовидной структурой
Дерево к дереву это, не тот стиль Ну и менять надо менять.
<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: Наконец-то появился плагин, ха-ха 😂).
Требование 5: Разрешить пользователям настраивать шаблон отображения
Это требование - позволить пользователям писать шаблоны сами по себе. Да, да, это позволить пользователям писать шаблоны самостоятельно. Ну, это одинаковое требование, что и изменение цвета интерфейса в соответствии с корпусом мобильного телефона пользователя. После чтения динамических компонентов и Асинхронные компоненты, я попробовал несколько самостоятельно. Способ по-прежнему не добру. Позже я увидел ответ большого парня на форуме сообщества (есть действительно люди, которые имеют те же нужды, как я (┬_┬)),это адрес, реализуется следующим образом:
<template>
<component :is="dynComponent" v-bind="data"></component>
</template>
<script>
export default {
name: "test",
props: ['test'],
data() {
return {
template: '',
data: {}
}
},
created() {
this.getTemplate() // 获取模版
},
methods: {
getTemplate() {
this.$axios.get('http://localhost:8080/static/test.json').then((result) => {
this.template = result.template;
this.data = result;
});
}
},
computed: {
dynComponent() {
const template = this.template ? `<div>${this.template}</div>` : `<div>nothing here yet</div>`;
return {
template, // template 就是模版
props: ['data'] // 传入数据
}
},
}
}
</script>
<style scoped lang="stylus">
</style>
Поскольку JS является однопоточным, страница будет отображаться до возврата асинхронного шаблона.Этот метод использует адаптивную функцию вычисляемых свойств, чтобы позволить Vue выполнить повторную визуализацию после извлечения шаблона. Дай этому большому парню колено. Если у вас есть лучший способ, пожалуйста, дайте мне знать, большое спасибо!
Требование 6: Не удалось загрузить удаленный образ, установите образ по умолчанию
Некоторые изображения могут быть взяты с другого веб-сайта или чего-то еще. В любом случае, все друзья-даосы понимают. В настоящее время необходимо установить изображение по умолчанию. Реализация выглядит следующим образом:
<template>
<div>
<img v-bind:src="imgUrl" @error="handleError" alt="">
</div>
</template>
<script>
export default {
name: "userList",
data() {
return {
imgUrl: 'url of the image'
}
},
methods: {
handleError(e) {
e.target.src = '/static/default.png'
}
}
}
</script>
<style scoped lang="stylus">
</style>
существуетstatic
Поместите изображение по умолчанию в папку, а затем обработайте его.img
изonerror
событие, будетsrc
настраиватьstatic
Путь к изображению по умолчанию в .
Требование 7: В одностраничном приложении предыдущий запрос должен завершаться при переключении страниц
Это требование очень разумно! Если вы не завершите предыдущий запрос, вы можете увидеть всплывающие подсказки после успешного (или неудачного) предыдущего запроса на новой странице, что определенно неразумно. Как этого добиться? axios предоставляет методы для отмены запросов:
Но тут есть небольшая проблема.Запросов на страницу может быть много, поэтому нельзя отменять по одному при переключении страниц (и вы не знаете какие именно интерфейсы вызываются),Пользователи сети здесь предлагают метод, я сделал некоторые оптимизации:
init() {
let self = this;
//配置全局取消数组
window.__axiosPromiseArr = [];
//请求拦截
this.$axios.interceptors.request.use(function (config) {
//为每个请求设置 cancelToken
config.cancelToken = new self.$axios.CancelToken(cancel => {
window.__axiosPromiseArr.push({cancel}) //放入一个全局数组,以便之后统一取消
});
return config;
}, function (error) {
return Promise.reject(error);
});
//响应拦截
this.$axios.interceptors.response.use((response) => {
switch (response.status) {
case 204:
this.$message.success('操作成功!');
break;
default:
return response.data;
}
}, (error) => {
if (error.message === 'cancel') {
//终止请求会抛出一个错误,捕获一下,不让其显示在控制台
}
});
},
Затем в стороже маршрута:
vueRouter.beforeEach((to, from, next) => {
//路由切换时终止所有请求
let axiosPromiseArr = window.__axiosPromiseArr;
if (axiosPromiseArr) {
console.log(axiosPromiseArr);
let len = axiosPromiseArr.length;
while (len--) { //从后向前终止请求,并删除 cancelToken,避免数组索引带来的问题
axiosPromiseArr[len].cancel('cancel');
axiosPromiseArr.splice(len, 1);
}
//或者:window.__axiosPromiseArr = [];
}
next()
});
В настоящее время этот метод кажется практичным.Если у вас есть лучший метод, пожалуйста, оставьте сообщение и дайте мне знать.
последняя напыщенная речь
В этой статье приведены некоторые советы, которые я недавно использовал. Если у вас есть лучший метод реализации, вы можете оставить сообщение для обсуждения в области комментариев, и вы можете указать на любые ошибки в тексте. Давайте учиться вместе (из конечно, если у вас есть какие-то сложные потребности, вы можете оставить сообщение и обсудить решения вместе) Схема 😄), эта статья будет время от времени обновляться, просто относитесь к ней как к блокноту 😏.