предисловие
В этой статье описывается личный опыт и идеи использования Vue для разработки проектов с момента окончания учебы и одного года официальной работы.Это только личное резюме.Если есть что-то необоснованное, пожалуйста, жалуйтесь. Ниже приводится общее содержание этой статьи.
1. Создание проекта Vue
1.1 От VueCli2 до VueCli3
В начале стажировки скаффолдом для Vue была версия VueCli2, и изученная конфигурация webpack тоже была Cli2, позже компания использовала Cli3, так что был процесс обучения и адаптации.
Разница между VueCli2 и VueCli3, вероятно, отражена в:
- Создать проект
3.0: vue создать.
2.0: веб-пакет vue init
- Стартовый проект
3.0 запустить npm запустить подачу
2.0 Запустить npm run dev
- Путь конфигурации проекта
2.0 Конфигурации типа webpack, multi-environment и упаковки проекта выполняются в папках config и build
Структура проекта 3.0 проще, чем 2.0, а файлы сборки и конфигурации отсутствуют.Вы можете создать файл vue.config.js на том же уровне, что и package.json для настройки.
Основные общие конфигурации организованы следующим образом:
// vue.config.js 基本配置方法
module.exports = {
// 项目部署的基础路径
// 我们默认假设你的应用将会部署在域名的根部,
// 比如 https://www.my-app.com/
// 如果你的应用时部署在一个子路径下,那么你需要在这里
// 指定子路径。比如,如果你的应用部署在
// https://www.foobar.com/my-app/
// 那么将这个值改为 `/my-app/`
// 基本路径 baseURL已经过时
publicPath: './',
// 打包项目时构建的文件目录,用法与webpack本身的output.path一致
outputDir: 'dist',
// 静态资源目录 (js, css, img, fonts)
assetsDir: 'assets',
// eslint-loader 是否在保存的时候检查,编译不规范时,设为true在命令行中警告,若设为error则不仅警告,并且编译失败
lintOnSave: true,
// 调整内部的 webpack 配置。查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
chainWebpack: () => {},
configureWebpack: () => {},
// vue-loader 配置项 https://vue-loader.vuejs.org/en/options.html
vueLoader: {},
// 生产环境是否生成 sourceMap 文件,默认true,若不需要生产环境的sourceMap,可以设置为false,加速生产环境的构建
productionSourceMap: true,
// css相关配置
css: {
// 是否使用css分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用<style>方式内联至html文件中
extract: true,
// 是否在构建样式地图,false将提高构建速度
sourceMap: false,
// css预设器配置项
loaderOptions: {},
// 启用 CSS modules for all css / pre-processor files.
// 这个选项不会影响 `*.vue` 文件
modules: false
},
// 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader`
// 在多核机器下会默认开启。
parallel: require('os').cpus().length > 1,
// 是否启用dll See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#dll-mode
dll: false,
// PWA 插件相关配置 see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa: {},
// webpack-dev-server 相关配置
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',//如果是真机测试,就使用这个IP
port: 1234,
https: false,
hotOnly: false,
proxy: null, // 设置代理
// proxy: {
// '/api': {
// target: '<url>',
// ws: true,
// changOrigin: true
// }
// },
before: app => {}
},
// 第三方插件配置
pluginOptions: {
// ...
}
}
1.2 Вторичная упаковка Axios
Назначение вторичной упаковки axios в основном заключается в трех аспектах:
- Обработка перехвата запросов интерфейса (обработка конфигурации, перехват повторных запросов)
- Обработка перехвата ответа интерфейса
- Инкапсуляция и повторное использование методов API
1.2.1 Обработка перехвата запроса интерфейса
1.2.1.1 Настраиваемые элементы
При выполнении перехвата запросов интерфейса и обработки конфигурации можно гибко настроить следующие параметры.
| параметр | значимость | пример |
|---|---|---|
| url | URL-адрес сервера, используемый для запроса | url: '/user' |
| method | Метод, используемый при создании запроса | method: 'get' |
| baseURL | автоматически добавляется вurlспереди, если толькоurlявляется абсолютным URL-адресом, установивbaseURLУдобен для передачи относительных URL-адресов в методы экземпляра axios. |
baseURL: 'some-domain.com/api/' |
| transformRequest | Разрешить изменение данных запроса перед отправкой на сервер // можно использовать только в методах запроса PUT, POST и PATCH // Функция в следующем массиве должна возвращать строку, или ArrayBuffer, или Stream |
transformRequest: [function (data) { // Произвольное преобразование данных |
| return data; }], |
||
| headers | Пользовательские заголовки для отправки | headers: {'X-Requested-With': 'XMLHttpRequest'}, |
| params | Параметры URL для отправки с запросом | params: { ID: 12345 }, |
| paramsSerializer | Быть ответственным заparamsСериализованные функции (например,www.npmjs.com/package/qs, API.jQuery.com/jQuery Страшные люди...) |
paramsSerializer: function(params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) } |
| data | dataданные отправляются как тело запросаПрименяется только к этим методам запроса «PUT», «POST» и «PATCH». без настройки transformRequest, должен быть одним из следующих типов:- string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams - Эксклюзивно для браузера: FormData, File, Blob - Эксклюзивно для узла: поток |
data: { firstName: 'Fred' } |
| timeout | Указывает количество миллисекунд, по истечении которого запрос истечет (0 означает отсутствие тайм-аута) | timeout: 1000 |
| adapter | Позволяет настраивать обработку запросов, чтобы упростить тестирование, возвращая обещание и применяя действительный ответ. | adapter: function (config) { /* ... */ }, |
| auth | Указывает, что следует использовать базовую аутентификацию HTTP, и предоставляет учетные данные, которые будут устанавливатьAuthorizationзаголовок, перезаписывает любое существующее использованиеheadersнастройка параметровAuthorizationголова |
auth: { username: 'janedoe', password: 's00pers3cret' }, |
| responseType | Тип данных ответа сервера, который может быть «arraybuffer», «blob», «document», «json», «text», «stream». | responseType: 'json', // по умолчанию |
| xsrfCookieName | Имя файла cookie, используемого в качестве значения токена xsrf. | xsrfCookieName: 'XSRF-TOKEN' |
| xsrfHeaderName | Имя заголовка HTTP, содержащего значение токена xsrf. | xsrfHeaderName: 'X-XSRF-TOKEN', // по умолчанию |
| onUploadProgress | Разрешить обработку событий прогресса загрузки | onUploadProgress: function (progressEvent) { // Обработка нативных событий прогресса }, |
| onDownloadProgress | Разрешить обработку событий прогресса для загрузки | onDownloadProgress: function (progressEvent) { // Обработка нативных событий прогресса }, |
| maxContentLength | Определяет максимально допустимый размер содержимого ответа | maxContentLength: 2000 |
| validateStatus | Определяет обещание разрешения или отклонения для заданного кода состояния ответа HTTP. еслиvalidateStatusвернутьtrue(или установить наnullилиundefined), обещание будет разрешено; в противном случае обещание будет отклонено |
validateStatus: function (status) { вернуть статус >= 200 && статус }, |
| maxRedirects | Определяет максимальное количество перенаправлений в node.js. | maxRedirects: 5, // по умолчанию |
| httpAgent |
httpAgentа такжеhttpsAgentИспользуется в node.js для определения пользовательских прокси-серверов, которые будут использоваться при выполнении http и https соответственно. |
httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), |
| proxy | Определите имя хоста и порт прокси-сервера | proxy: { host: '127.0.0.1', port: 9000, auth: : { username: 'mikeymike', password: 'rapunz3l' } }, |
| cancelToken | Указывает токен отмены, используемый для отмены запроса. | cancelToken: new CancelToken(function (cancel) { }) |
1.2.1.2 Перехват повторяющихся запросов
В случае, когда скорость сети низкая, у пользователя может быть больше щелчков и повторных запросов, которые создают проблему дрожания страницы, пользовательский интерфейс не очень хорош, поэтому процесс перехватывает повторный запрос. Идея такова:
Создать очередь запросов ---->
-----Обработка перехвата------
Идентифицирует предстоящий запрос ---->
Определить, совпадает ли отправляемый запрос с запросом в очереди ---->
Если это то же самое, выполните метод отмены текущего запроса и удалите его из очереди запросов---->
Создайте метод отмены, который будет запрошен, и поместите его в очередь.
Обработка перехвата
request.interceptors.request.use(
config => {
// 拦截重复请求(即当前正在进行的相同请求)
const requestData = getRequestIdentify(config, true); // 标识请求
removePending(requestData, true);// 取消重复请求
config.cancelToken = new CancelToken((c) => { // 创建当前请求的取消方法
pending[requestData] = c;
});
return config;
}, error => {
return Promise.reject(error)
})
Запрос личности
const getRequestIdentify = (config, isReuest = false) => {
let url = config.url;
if (isReuest) {
url = config.baseURL + config.url.substring(1, config.url.length);
}
return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data));
};
Отменить дублирующий запрос
const pending = {};
const CancelToken = axios.CancelToken;
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]('取消重复请求');
}
delete pending[key];
};
1.2.2 Обработка перехвата ответа интерфейса
Перехват ответа интерфейса в основном предназначен для извлечения, инкапсуляции и использования данных, возвращаемых интерфейсом, и выполнения унифицированной обработки конфигурации для исключений запроса.
request.interceptors.response.use(
response => {
const data = response.data || {};
return data;
},
error => {
const code = error.response.status;
if (code) {
let msg = '';
switch (code) {
case 400:
msg = '请求错误';
break;
case 401:
msg = '未授权,请登录';
break;
case 403:
msg = '拒绝访问';
break;
case 404:
msg = `请求${error.response.config.url}出现404错误`;
break;
case 408:
msg = '请求超时';
break;
case 500:
msg = '服务器内部错误';
break;
case 501:
msg = '服务未实现';
break;
case 502:
msg = '网关错误';
break;
case 503:
msg = '服务不可用';
break;
case 504:
msg = '网关超时';
break;
case 505:
msg = 'HTTP版本不受支持';
break;
}
Message.error(msg);
}
return Promise.reject(error);
}
)
1.2.3 Инкапсуляция методов API
Метод запроса интерфейса инкапсулирован отдельно, параметр метода GET — params, параметр метода POST — данные.
// api.js
import request from '@/utils/request';
export function APIPostMethod(data) { // 自定义接口方法
return request({
url: '/url1',
method: 'post',
data
});
}
export function APIGetMethod(params) { // 自定义接口方法
return request({
url: '/url2',
method: 'get',
params
});
}
Вызов методов API в бизнесе
import { APIGetMethod, APIPostMethod } from '@/utils/request';
const params = {}
APIGetMethod(params).then(res => {
//...
//对数据处理
})
1.3 Междоменная обработка
Проще говоря, чтобы предотвратить атаки XSS и CSFR, ограничение политики браузера в отношении одного и того же источника приводит к междоменным проблемам, когда интерфейс и сервер разделены и разработаны. То есть, когда запрос и ответ не относятся к одному и тому же протоколу + доменному имени + порту, они не будут разрешены браузером. Однако политика того же происхождения является только политикой браузера, а не частью протокола HTTP, поэтому, когда сервер вызывает интерфейс HTTP, он использует только протокол HTTP, а не через браузер, и не будет выполняться. JS-скрипты, поэтому политика одного и того же источника не будет активирована, междоменная проблема отсутствует. Ввиду этой особенности вы можете начать с настройки внешнего интерфейса прокси-сервера.
Пробовал два способа решения междоменной проблемы:
1. Прокси промежуточного слоя Node.js
В vue-cli прокси-интерфейс node+webpack+webpack-dev-server используется для перекрестного домена.
//vue.config.js
const config = {
// ...
devServer: {
hot: true,
open: true,
host: '127.0.0.1',
// host: '0.0.0.0',//如果是真机测试,就使用这个IP
port: 8899,
https: false,
hotOnly: false,
proxy: {
'/': {
target: 'http://xxx.xxx.xx.xx:xxxx/',
logLevel: 'debug',
ws: false,
changOrigin: true
}
}
}
}
module.exports = config;
2. Обратный прокси Nginx
Настройте прокси-сервер через nginx.Доменное имя такое же, как у локального домена А, но другой порт.Обратный прокси обращается к интерфейсу под доменным именем пира Б.
#proxy服务器
server {
listen 8088;
server_name _;
client_max_body_size 50m;
location / {
root html/dist;
try_files $uri $uri/ /index.html;
index /index.html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ^~/api {
proxy_pass http://xxx.xxx.xx.xx:xxxx/api;
proxy_redirect default;
}
}
Когда фронтенд запускает проект, ему нужно поставить прокси проекта на соответствующий порт 8088 (мне кажется, что этот метод немного избыточен, но мои коллеги используют этот метод, мы не смеем сказать, поэтому я использую первый здесь).
Метод Nginx больше подходит для онлайн-развертывания для решения междоменного использования.В среде разработки удобно и быстро использовать devserve в vue-cli.
2. Вью-трюки
2.1 Ленивая загрузка
Ленивая загрузка, также известная как ленивая загрузка, позволяет загружать компоненты асинхронно. Цель состоит в том, чтобы отложить загрузку второстепенных ресурсов, сократить время загрузки страницы и оптимизировать производительность страницы.
2.1.1 Маршрут ленивый загрузка
export default new Router({
routes:[
{
path: '/test',
name: 'test',
//懒加载
component: resolve => require(['../page/test.vue'], resolve),
},
]
})
При ленивой загрузке маршрута код делится на разные блоки кода в соответствии с маршрутом, при переключении на соответствующий маршрут загружается соответствующий блок кода, и загрузка становится более эффективной.
2.1.2 Ленивая загрузка компонентов
components: {
UpdateModal: resolve => { require(['./UpdateNormalTaskModal'], resolve); }
},
На основе ленивой загрузки маршрутов проводится сравнительный эксперимент ленивой загрузки компонентов.
Ленивая загрузка компонентов не используется:
Вся страница представляет собой js размером 718 КБ и загружается 166 мс.
При использовании ленивой загрузки компонентов:
Вся страница разбита на три js (53.js), из которых два лениво загруженных компонента имеют по одному js (78.js, 91.js) Видно, что размер файла 53.js становится меньше , и загрузка Скорость становится быстрее.Разбиение одного js на несколько для параллельной загрузки может повысить эффективность загрузки и тем самым повысить производительность.
2.2 Внедрение по запросу
Загрузка по запросу обычно используется при использовании сторонней библиотеки, чтобы избежать чрезмерного давления на загрузку первого экрана, вызванного слишком большим размером сторонней библиотеки.
В качестве примера возьмем загрузку VantUI по запросу.
- Установите Babel-плагин-импорт
npm i babel-plugin-import -D - Настроить плагины (плагины) в babel.config.js
module.exports = {
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
],
presets: [
'@vue/app'
]
};
- Внедрить и использовать в файле vue, который использует сторонние компоненты
import { Swipe, SwipeItem, ImagePreview } from 'vant';
export default {
components: {
vanSwipe: Swipe,
vanSwipeItem: SwipeItem,
}
}
2.3 Использование локальных ресурсов изображений в JS
При использовании локальных ресурсов в синтаксисе v-bind vue путь является относительным путем относительно локальной папки, который не может быть разрешен во время упаковки.
2.3.1 Чтение статического ресурса
Поместите ресурсы изображения в папку статических ресурсов, при использовании src получите прямой доступ к ресурсам в корневом каталоге.
Например, картинка помещается в публичный каталог, а путь прямо пишется как'/img/case/gkjg/7.jpg'
2.3.2 Импорт ресурсов
Импортируйте ресурс изображения, затем используйте переменную imgurl в данных.
data(){
return {
imgUrl:require("../assets/test.png")
}
}
2.4 keep-alive
<keep-alive></keep-alive>Включенные компоненты будут кэшироваться, а DOM не будет повторно отображаться, тем самым экономя производительность.При переключении контента будут срабатывать две функции хука жизненного цикла, активированные и деактивированные, а кэшированные компоненты сохранят состояние текущего компонента.
2.4.1 Кэш страниц маршрутизации
Используйте метаполе маршрутизатора
//...router.js
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: '/page1',
name: 'Page1',
component: Page1,
meta: {
keepAlive: true // 需要被缓存
}
}
]
})
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
2.4.2 Кэш компонентов
<keep-alive include="test-keep-alive">
<!-- 将缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
<keep-alive include="a,b">
<!-- 将缓存name为a或者b的组件,结合动态组件使用 -->
<component :is="view"></component>
</keep-alive>
<!-- 使用正则表达式,需使用v-bind -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<keep-alive exclude="test-keep-alive">
<!-- 将不缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
2.4.3 Объединение доRouteEnter
комбинированная маршрутизацияbeforeRouteLeave(to, from, next)крючок, наборto.meta.keepAliveчтобы указать, выполняет ли целевая страница keepAlive.
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = true; // 当前页面跳转到下一个页面时,让目的页面缓存,不刷新
next();
}
};
Надлежащее использование поддержки активности в сочетании с активированными и деактивированными функциями перехватчика, кэширование содержимого, которое не нужно обновлять, и размещение содержимого, которое необходимо обновить, в двух перехватчиках для обработки, что может уменьшить количество ненужных HTTP-запросов и повторяющихся DOM. rendering , что значительно улучшило производительность
2.5 Пользовательские директивы v-модели
2.5.1 Обычная v-модель
- Сценарии применения
Некоторые состояния будут изменены как в дочернем компоненте, так и в родительском компоненте.В то же время дочерний и родительский компоненты будут писать некоторую бизнес-логику при изменении состояния.Если вы используете реквизиты и мониторинг событий, код компонента будет очень сложно писать Это некрасиво, поэтому лично мне нравится использовать v-model для реализации двухсторонней привязки дочерних и родительских компонентов! Очень удобно!
- Роль v-модели
v-model часто используется в компонентах формы, таких как ввод, для реализации двусторонней привязки значений формы, что означает, что значение может быть передано в форму из внешнего мира. внешний мир также будет изменен, поэтому их эффекты двунаправлены.
-
Суть v-модели
Суть v-model на самом деле является синтаксическим сахаром, например, в элементе ввода
<!--v-model是语法糖-->
<Input v-model="username">
<!--默认等效于下⾯面这⾏行行-->
<Input :value="username" @input="username=$event">
2.5.2 Пользовательская v-модель
Поскольку v-model — это синтаксический сахар, фактическийpropsа также$emitНепременно, но на какой propd указывает v-model, соответствующий$emitЧто это за событие, нам нужно его объявить.
Возьмите демо в качестве примера:
// 父组件中对demo组件的调用
<demo v-model="currentData" :other="otherProps"></demo>
Привязать значение к родительскому компоненту
// demo.vue
<template>
<div>
<button @click="cEvent">点击</button>
{{ current }}
</div>
</template>
<script>
export default {
props: {
current: {
type: String
},
other: {
type: String
}
},
watch:{
current(newVal,oldVal) {
...doSomething...
}
},
model: {
prop: 'current',
event: 'event'
},
methods: {
cEvent() {
this.$emit('event', '改变啦');
}
}
};
</script>
Поведение по умолчанию при изменении модели объявлено в подкомпоненте, указавv-modelВходящийpropsсерединаcurrenт, когда менятьcurrentценность ,$emitСобытие события уведомляет родительский компонент об измененияхcurrentценность, реализованнаяv-modelдвухсторонняя привязка.
На самом деле, приведенное выше поведение эквивалентно следующему при использовании обычногоpropsа также$emitнаписание
// 父组件中对demo组件的调用
<template>
<div>
<demo :current"currentData" :other="otherProps" @event="changeCurrent"></demo>
</div>
</template>
<script>
export default {
data() {
return {
currentData: '初始值'
}
},
methods: {
changeCurrent(val) {
this.changeCurrent = val
...doSomething...
}
}
};
</script>
// demo.vue
<template>
<div>
<button @click="cEvent">点击</button>
{{ current }}
</div>
</template>
<script>
export default {
props: {
current: {
type: String
},
other: {
type: String
}
},
watch:{
current(newVal,oldVal) {
...doSomething...
}
}
methods: {
cEvent() {
this.$emit('event', '改变啦');
}
}
};
</script>
2.6 Динамические компоненты + автоматическая регистрация компонентов
Сцены:
- Необходимо динамически загружать vue-файлы папки
- Файлов много, вручную импортировать не хочется
2.6.1 Автоматическая регистрация
// 此段代码直接写在script标签里
var templatePage = {};
var nameList = [];
function initPage() {
/*
require.context()的参数中,不能含有变量
因此不能动态导入,只能一次性导入之后,动态引用
*/
const requireComponent = require.context(
// 其组件目录的相对路径
'../views/template',
// 是否查询其子目录
true,
// 匹配基础组件文件名的正则表达式
/.vue$/
);
requireComponent.keys().forEach(fileName => {
var names = fileName
.split('/')[1] + '-' + fileName
.split('/')[2].split('.')[0];
const componentConfig = requireComponent(fileName);
templatePage[names] = componentConfig.default || componentConfig;
nameList.push(names);
});
}
initPage();
components: {
...templatePage
},
Этот метод также можно использовать для автоматического импорта ресурсов изображений.
2.6.2 Динамические компоненты
Компоненты сопоставляются по is, поэтому эти компоненты должны иметь имя.
// template中
<component
:is="comp"
v-for="comp in fileList"
:id="comp"
:key="comp"
class="content__item">
</component>
computed: {
fileList() {
return nameList
.filter(v => v.split('-')[0] === this.$route.params.templateId);
}
},
2.7 Инкапсуляция пользовательских инструкций
официальная документация
Управление разрешениями на уровне кнопок реализовано с помощью пользовательской инструкции v-permission.
Эта директива фильтрует переданный массив разрешений и текущий массив ролей пользователя.
Можно увидеть, есть ли у пользователя необходимые разрешения, в противном случае удалить элемент dom, подключенный к директиве
// permission.js
export default {
// el - 挂载dom
// binding- v-per="[]" {value: []}
inserted(el,binding) {
// 获得传入的值
const {value: permissionRoles} = binding;
//...业务逻辑
// 获取用户角色
const roles = stroe.getters.roles;
// 合法性判断
if(permissionRoles && permissionRoles instanceof Array
permissionRoles.length > 0) {
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
});
if(!hasPermission) {
el.parentNode && el.parentNode.removeChild(el);
}
} else {
throw new Error('需要指定数组类型的权限');
}
}
}
//main.js
import permission from '@../permission;
//注册指令
Vue.directive('permission',permission);
2.8 Декораторы
Синтаксис Decorator не прошел предложение, поэтому редко используется в проекте.
blog.CSDN.net/WeChat_3423…
Наггетс.Талант/пост/685651…
2.9 Индикатор загрузки файлов
Используйте axios onUploadProgress и onDownloadProgress, чтобы получить информацию о ходе загрузки и загрузки соответственно.Метод настройки такой же, как и при настройке конфигурации, такой как contentType.
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
}
// `onDownloadProgress` allows handling of progress events for downloads
// browser only
onDownloadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
}
3. Element-UI наступает на яму
UI-библиотека системы управления фоном компании — Element, За это время я наступил на множество ям, но был занят делами и пренебрегал их организацией.
3.1Dialog
3.1.1 Жизненный цикл (в сценарии Диалога как подкомпонента)
Порядок загрузки и отрисовки дочернего и родительского компонентов в жизненном цикле:родитель beforeCreate->родитель создан->родитель beforeMount->дочерний элемент beforeCreate->дочерний элемент создан->дочерний элемент beforeMount->дочерний элемент смонтирован->родительский элемент
Поэтому после того, как эл-диалог создан в родительском компоненте, он также входит в созданный и смонтированный цикл, независимо от того, открыт диалог или нет, он появляется в интерфейсе.
Есть много поворотов, чтобы иметь возможность выполнить метод, который получает данные при его открытии.
1.v-if управление методом рендеринга
В родительском компоненте el-dialog управляется v-if, который будет перерисовываться каждый раз при его отображении. Затем вы можете выполнить метод, который хотите выполнить в созданном жизненном цикле.
2. метод мониторинга часов
В компоненте el-dialog монитор управляет отображением скрытых переменных.Когда переменная имеет значение true, она представляет диалоговое окно и выполняет выполняемый вами метод.
3. открыть метод обратного вызова события
Не упоминайте об этом, я не обнаружил, что el-dialog имеет функцию обратного вызова, вызываемую open раньше! Только с двумя вышеуказанными методами используйте обратный вызов oepn, выполните свой метод в обратном вызове, и все готово!
3.1.2 Перетаскивание
Измените диалоговый компонент element-ui, чтобы его можно было перетаскивать, и пользователи могли перетаскивать его, чтобы определить местоположение диалогового окна в соответствии с фактическими потребностями.
Реализация реализациибольшие пользователи сетиКод , отменяет решение о границе в соответствии с фактическим спросом
Конкретная реализация выглядит следующим образом:
- Создайте файл directives.js
import Vue from 'vue';
// v-dialogDrag: 弹窗拖拽
Vue.directive('dialogDrag', {
bind(el, binding, vnode, oldVnode) {
// 获取拖拽内容头部
const dialogHeaderEl = el.querySelector('.el-dialog__header');
// 获取拖拽内容整体 这个rrc-dialog是我自己封装的组件 如果使用element的组件应写成.el-dialog
const dragDom = el.querySelector('.el-dialog');
dialogHeaderEl.style.cursor = 'move';
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
// 鼠标按下事件
dialogHeaderEl.onmousedown = (e) => {
e.stopPropagation();
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
// 获取到的值带px 正则匹配替换
let styL, styT;
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (sty.left.includes('%')) {
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
} else {
styL = +sty.left.replace(/\px/g, '');
styT = +sty.top.replace(/\px/g, '');
}
// 鼠标拖拽事件
document.onmousemove = function(e) {
e.stopPropagation();
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
const l = e.clientX - disX;
const t = e.clientY - disY;
let finallyL = l + styL;
let finallyT = t + styT;
// 边界值判定 注意clientWidth scrollWidth区别 要减去之前的top left值
// dragDom.offsetParent表示弹窗阴影部分
if (finallyL < 0) {
// finallyL = 0;
} else if (finallyL > dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft) {
finallyL = dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft;
}
if (finallyT < 0) {
// finallyT = 0;
} else if (finallyT > dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetTop) {
finallyT = dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetTop;
}
// 移动当前元素
dragDom.style.left = `${finallyL}px`;
dragDom.style.top = `${finallyT}px`;
// 将此时的位置传出去
// binding.value({x:e.pageX,y:e.pageY})
};
document.onmouseup = function(e) {
e.stopPropagation();
document.onmousemove = null;
document.onmouseup = null;
};
};
}
});
- Представлено в main.js
import './utils/directives';
- Используйте команду v-dialogDrag в теге диалога, который необходимо перетащить, чтобы выполнить перетаскивание.
<el-dialog v-dialogDrag custom-class="enter-dialog" title="""></el-dialog>
3.2 Охватывающий выбор выбора резерва таблицы
Атрибут резервного выбора в el-table может реализовывать переключение страниц, но при этом сохранять данные, проверенные на предыдущей странице (для межстраничного выбора).
Примечание:
1. Установите ключ строки.Этот ключ строки должен быть уникальным идентификатором для каждой части данных.
2. Когда дело доходит до переключения содержимого таблицы, вы не хотите, чтобы его сохраняли и проверяли, но когда повторный рендеринг не выполняется, вы должны помнить, чтобы снять проверку, чтобы избежать неправильной работы.
3.3 Пользовательские стили
3.3.1 Проникновение стиля
При использовании компонентов Element в VUE в файле компонента каждый компонент обычно используется для предотвращения загрязнения стиля именования, обычно используется scot. В этом случае, если вы хотите настроить компоненты ELMENT, вы можете использовать три способа:
<style scoped>
/* 外层>>>穿透 */
.my-dialog >>> .el-dialog {
background: #ff0;
}
/* /deep/穿透 */
/deep/ .el-dialog{
background: #ff0;
}
</style>
/* 单独一个不使用scoped的style用来设置第三方样式的修改*/
/* 全局css配合外层样式限制 */
<style>
.my-dialog .el-dialog{
background: #ff0;
}
</style>
При отладке видно, что эффект изменения стиля компонента элемента только внутри компонента был достигнут, а глобальный стиль компонента элемента затронут не будет:
- При ограничении внешний слой >>> проникает, и уникальный идентификатор [data-v-***] добавляется к внешнему слою
- /deep/penetration, идентификатор добавляется непосредственно перед компонентом el
- Нет ничего особенного в установке одной глобальной метки и установке стиля во внешнем слое для локальных ограничений, но это также дает ожидаемый эффект.
- Приоритет три: >>> Проникновение > Внешний стиль Global Fit > /deep/

Примечание:
- /deep/ является собственным стандартом Chrome и может не работать в других браузерах.
> Так что сейчас я использую третий метод (отдельно установить тег стиля), хотя код немного уродлив, но это все еще относительно стабильный метод.
3.3.2 Специальные стили раскрывающихся списков
яма:
При настройке стиля компонента, содержащего раскрывающийся список, например раскрывающегося списка el-select, часть раскрывающегося списка отделяется от файла компонента и находится непосредственно на одном уровне с самым внешним App.vue, это означает, что любой внешний стиль обернут. Не используйте этот раскрывающийся список. Следовательно, проникновения нет, и как бы вы ни проникали в него, вы не сможете проникнуть в него.
Официальная документация:
Измените стиль в разделе propper-class. В настоящее время можно использовать только стиль без области действия. Чтобы не влиять на раскрывающийся список на каждой странице, имя класса propper-class должно быть уникальным.
Компоненты с атрибутами правильного класса, вероятно:
3.4 Image
3.4.1 Белый фон по умолчанию при загрузке
При загрузке изображение будет иметь белый фон по умолчанию, и трудно понять, какой CSS делает трюк в момент загрузки, поэтому мне нужно перейти к исходному коду, чтобы увидеть это.// node_modules/element-ui/packages/image/src/main.vue
Я видел, что класс el-image__placeholder действует как демон. На данный момент вы можете переписать стиль в свой собственный компонент и изменить его на желаемый цвет! !