Проблемы и решения, которые затрагивают ваши болевые точки в проекте Vue (обновлено)

внешний интерфейс сервер JavaScript Vue.js vue-router
Проблемы и решения, которые затрагивают ваши болевые точки в проекте Vue (обновлено)

В последнее время требуется использовать Vue для разделения передней и задней частей для разработки общедоступных учетных записей WeChat.После постоянного изучения и наступания на яму я резюмировал следующие распространенные проблемы и решения при разработке проектов Vue. Если вы vue-босс, пожалуйста, игнорируйте скромное мнение младшего брата ^V^

  • Перечислите проблемы с передачей параметров для входа на страницу сведений.
  • Проблема запроса междоменного интерфейса сервера в локальной среде разработки
  • Унифицированное управление инкапсуляцией axios и интерфейсом API
  • Загрузка библиотек пользовательского интерфейса по требованию.
  • Как элегантно переопределить стили компонентов в библиотеке пользовательского интерфейса только на текущей странице
  • проблема с таймером
  • проблема с импортом rem-файла
  • Vue-Awesome-Swiper может в основном решить все ваши потребности в карусели.
  • Проблема генерации большого .map файла после упаковки
  • Решение fastClick с задержкой 300 мс
  • Порядок, в котором параметры записываются в компоненте
  • Ленивая загрузка маршрута (также называемая ленивой загрузкой)
  • Включить код сжатия gzip
  • Практика возврата на страницу списка со страницы сведений для кэширования данных и местоположения просмотра, а также других страниц, входящих на страницу списка для очистки данных.
  • Частные области видимости CSS и глубокие селекторы
  • Гипер открытый тест скорости
  • Два способа получить данные vue + скелетный экран
  • Двусторонняя привязка данных пользовательских компонентов (родительско-дочерние компоненты)
  • Управление разделением маршрутизации
  • миксины упрощают общие операции
  • После упаковки файлы, изображения и ресурсы фонового изображения не существуют или указан неверный путь.
  • Разработка плагина Vue, публикация на github, настройка отображаемого адреса, публикация пакета npm

============================ Это великолепная разделительная линия~~============== ===========

Перечислите проблемы с передачей параметров для входа на страницу сведений.

Например, чтобы перейти на страницу сведений о продукте со страницы списка продуктов, вам необходимо передать идентификатор продукта;

<router-link :to="{path: 'detail', query: {id: 1}}">前往detail页面</router-link>

Путь к c-страницеhttp://localhost:8080/#/detail?id=1, вы можете видеть, что передается параметр id=1, и идентификатор все равно будет существовать, даже если страница будет обновлена. В настоящее время соответствующие подробные данные можно получить через идентификатор на странице c. Способ получения идентификатораthis.$route.query.id

Методы передачи параметров Vue: запрос, параметры + параметры динамической маршрутизации.

Назовите разницу между ними:

1. query переключает маршрутизацию по пути, params переключает маршрутизацию по имени

// query通过path切换路由
<router-link :to="{path: 'Detail', query: { id: 1 }}">前往Detail页面</router-link>
// params通过name切换路由
<router-link :to="{name: 'Detail', params: { id: 1 }}">前往Detail页面</router-link>

2. запрос пройденthis.$route.queryЧтобы получить параметры, params получает параметры через this.$route.params.

// query通过this.$route.query接收参数
created () {
    const id = this.$route.query.id;
}

// params通过this.$route.params来接收参数
created () {
    const id = this.$route.params.id;
}

3. способ представления URL-адресов параметров запроса: /detail?id=1&user=123&identity=1&more параметры

params + url метод динамической маршрутизации: /detail/123

4.params параметры динамической маршрутизации, должны определять параметры в маршруте, а затем должны добавлять параметры при переходе маршрута, иначе это будет пустая страница:

{      
    path: '/detail/:id',      
    name: 'Detail',      
    component: Detail    
},

Обратите внимание, что когда параметры params передаются, если в маршруте не определены никакие параметры, они могут быть переданы и получены одновременно, но после обновления страницы этот параметр не существует. Это не работает для поведения, которое зависит от параметров для определенных операций, потому что вы не можете всегда просить пользователя не обновлять страницу. Например:

// 定义的路由中,只定义一个id参数
{
    path: 'detail/:id',
    name: 'Detail',
    components: Detail
}

// template中的路由传参,
// 传了一个id参数和一个token参数
// id是在路由中已经定义的参数,而token没有定义
<router-link :to="{name: 'Detail', params: { id: 1, token: '123456' }}">前往Detail页面</router-link>

// 在详情页接收
created () {
    // 以下都可以正常获取到
    // 但是页面刷新后,id依然可以获取,而token此时就不存在了
    const id = this.$route.params.id;
    const token = this.$route.params.token;
}


Проблема запроса междоменного интерфейса сервера в локальной среде разработки


Приведенный выше отчет об ошибке знаком всем, сообщение об ошибке означает, что нет прав доступа (междоменная проблема). Когда локальный проект разработки запрашивает интерфейс сервера, политика одного и того же происхождения клиента приводит к междоменным проблемам.

Давайте сначала продемонстрируем ситуацию, когда нет конфигурации, разрешающей локальный междоменный доступ:




Как видите, в этот момент мы нажимаем, чтобы получить данные, и браузер предлагает нам междоменный переход. Поэтому мы не можем получить доступ к данным.

Затем мы продемонстрируем сбор данных после настройки разрешения междоменного доступа:


Примечание. После настройки обязательно закройте исходный сервер и перезапустите его.npm run devСтартовый проект. В противном случае недействительно.



Настраиваем разрешать локальный кросс-домен на 1, а на 2 обращаем внимание при обращении к интерфейсу пишем/api, здесь/apiЭто относится к доменному имени интерфейса, который мы хотим запросить. Если мы не хотим приносить это каждый раз/api, вы можете изменить конфигурацию axios по умолчаниюaxios.defaults.baseURL = '/api';Таким образом, мы можем запросить интерфейс напрямуюthis.$axios.get('app.php?m=App&c=Index&a=index'), очень просто иметь древесину. На этом этапе, если вы просмотрите запрос xhr в сети, вы обнаружите, что отображается адрес запроса localhost:8080/api. Это не суета, просто прокси:



Ну и напоследок прикрепите код proxyTable:

proxyTable: {
      // 用‘/api’开头,代理所有请求到目标服务器
      '/api': {
        target: 'http://jsonplaceholder.typicode.com', // 接口域名
        changeOrigin: true, // 是否启用跨域
        pathRewrite: { //
          '^/api': ''
        }
      }
}

Примечание. После настройки обязательно закройте исходный сервер и перезапустите его.npm run devСтартовый проект. В противном случае недействительно.


Унифицированное управление инкапсуляцией axios и интерфейсом API

Инкапсуляция аксиом в основном используется, чтобы помочь нам перехватывать запросы и перехватывать ответы.

При перехвате запроса мы можем использовать userToken, заголовок почтового запроса, сериализацию данных отправки поста с помощью qs и т. д.

При перехвате ответа мы можем выполнять унифицированную обработку ошибок по коду состояния и так далее.

Единое управление интерфейсом axios — необходимый процесс при выполнении проектов. Это облегчает нам управление нашим интерфейсом, и нам не нужно возвращаться к нашему бизнес-коду, чтобы изменить интерфейс при его обновлении.

Поскольку здесь немного больше контента, поместите его в другую статью,Отправить ссылку сюда.


Загрузка UI-библиотек по требованию:

Почему вы хотите использовать метод загрузки по требованию вместо того, чтобы ввести все это сразу, я не буду много говорить. Вот пример загрузки vant по требованию, чтобы продемонстрировать, как библиотека пользовательского интерфейса в vue выполняет загрузку по требованию:

  • Установить: cnpm i vant -S
  • Установите плагин babel-plugin-import, чтобы загружать его по требованию:cnpm i babel-plugin-import -D
  • Добавьте конфигурацию плагина в файл .babelrc:

libraryDirectory { 
    
    "plugins": [ 
        // 这里是原来的代码部分
        // …………

        // 这里是要我们配置的代码
        ["import", 
            { 
                "libraryName": "vant", 
                "libraryDirectory": "es", 
                "style": true 
            }
        ] 
    ] 
}
  • Загрузите нужные вам плагины по запросу в main.js:

// 按需引入vant组件
import {   
    DatetimePicker,   
    Button,   
    List 
} from 'vant';
  • Используйте компоненты:

// 使用vant组件
Vue.use(DatetimePicker)  
    .use(Button)  
    .use(List);
  • Наконец, на странице используйте:

<van-button type="primary">按钮</van-button>

PS: В дополнение к библиотеке Vant, таких как Antiui, Elementui и т. Д., Многие библиотеки пользовательских интерфейсов поддерживают нагрузку на требование, вы можете перейти к документации, которая будет упомянута выше. По сути, он поддерживает нагрузку по требованию, устанавливая плагин для импорта Babel-Plugin-Import. Использование такое же, как для Vant, поэтому вы можете использовать его.


Как элегантно переопределить стили компонентов в библиотеке пользовательского интерфейса только на текущей странице

Во-первых, стили наших vue-файлов записываются в тег , и добавляется scoped, чтобы стиль действовал только на текущей странице. Тогда возникает вопрос, посмотрите на картинку:


Все стили, которые мы обычно пишем, будут добавлены с атрибутом [data-v-23d425f8] (как показано в 1), но теги внутри сторонних компонентов не компилируются с атрибутом [data-v-23d425f8]. Поэтому, если мы хотим изменить стиль компонента, это невозможно. Что делать, некоторые друзья пишут класс для стороннего компонента, а потом прописывают тег стиля без атрибута socped в общедоступном css файле или на текущей странице, а потом прямо модифицируют стиль стороннего компонента в Это. Это подход, но есть проблемы с глобальным загрязнением и конфликтами имен. Соглашаясь с конкретным методом именования, вы можете избежать конфликтов именования. Но все же недостаточно элегантно.

как отличныймощный)показывать(сила)из(болезнь)вперед(страдать от)конец(К), как это можно допустить? Что ж, давайте поговорим об элегантном решении:

Решается селектором глубины. Например, измените компонент на рисунке выше.van-ellipsisСтиль класса, вы можете сделать это:

.van-tabs /deep/ .van-ellipsis { color: blue};

Результат после компиляции:


Это также не добавит атрибут [data-v-23d425f8] к van-ellipsis. На этом этапе вы можете легко изменять стили сторонних компонентов.

Конечно селектор глубины здесь/deep/потому что я используюlessязык, если вы не используетеless/sassподождите, вы можете использовать>>>символ.

Подробнее о селекторах глубины рассказывается далее в статье.


Проблема с таймером:

Я пишу таймер на странице а, пусть он печатает 1 каждую секунду, а затем переходит на страницу б. В это время мы видим, что таймер все еще выполняется. Это очень требовательно к производительности. Как показано ниже:




Обходной путь 1:

Во-первых, я определяю имя таймера в функции данных:

data() {            
    return {                              
        timer: null  // 定时器名称          
    }        
},

Затем используйте таймер следующим образом:

this.timer = (() => {
    // 某些操作
}, 1000)

Наконец, очистите таймер во время жизненного цикла beforeDestroy():

beforeDestroy() {
    clearInterval(this.timer);        
    this.timer = null;
}
Вариант 1 имеет два недостатка.Цитируя слова You Da:
  • это нужно сохранить в этом экземпляре компонентаtimer, желательно, чтобы к нему имели доступ только хуки жизненного цикла, если вы можете. Это не серьезная проблема, но ее можно считать беспорядком.
  • Наш код сборки не зависит от нашего кода очистки, что затрудняет программную очистку всего, что мы создаем.

Решение 2:

Метод заключается в очистке таймера по положению прослушивателя событий $once после определения таймера. Вот полный код:

const timer = setInterval(() =>{                    
    // 某些定时器操作                
}, 500);            
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {            
    clearInterval(timer);                                    
})

Вариант 2 хотел бы поблагодарить @zzx18023Решения, представленные в разделе комментариев. Подобно другим компонентам, которые необходимо использовать на текущей странице и оставить, чтобы быть уничтоженным (например, составляющие компоненты некоторых сторонних библиотек и т. Д.) Этот метод можно использовать для решения проблемы бега на заднем плане после ухода Отказ

В общем, мы рекомендуем использоватьВариант 2 делает код более читабельным и понятным с первого взгляда.если не ясно$once、$on、$offиспользуйте, вот адрес учебника официального сайта,в программных прослушивателях событий.


Проблема с импортом rem-файла:

Когда мы работаем над мобильным терминалом, адаптация — это проблема, которую необходимо решать. Например, наше решение для адаптации - написать rem.js, принцип очень прост, то есть вычислить размер шрифта html в соответствии с размером веб-страницы, в основном все знают, что код напрямую прикреплен здесь, и я не буду вводить это много.

;(function(c,d){var e=document.documentElement||document.body,a="orientationchange" in window?"orientationchange":"resize",b=function(){var f=e.clientWidth;e.style.fontSize=(f>=750)?"100px":100*(f/750)+"px"};b();c.addEventListener(a,b,false)})(window);

Вот как представить проблему, это очень просто. В main.js напрямуюimport './config/rem'Просто импортируйте. Путь импорта заполняется в соответствии с вашим путем к файлу.


Vue-Awesome-Swiper может в основном решить все ваши потребности в карусели.

Во многих используемых нами UI библиотеках (vant, antiUi, elementUi и т.д.) есть карусельные компоненты, которых достаточно для обычных карусельных эффектов. Однако иногда наш эффект карусели может быть ослепительным, а карусель в библиотеке пользовательского интерфейса не может этого сделать. Конечно, если с технологией и временем все в порядке, можно построить и более ослепительное колесо самостоятельно.

Здесь я расскажу о компонентах Carousel Vue-Awesome-Swiper, который действительно мощный и может в основном соответствовать нашим потребностям каруселя. Шкупалка считает, что многие люди использовали его, он очень прост в использовании, и нам также очень удобно разработать и настроить эффект карусели. Компонент Vue-Awesome-Swiper по существу основан наswiperДа или свайпер, который может запускаться в vue. Вот как это использовать:

  • Установитьcnpm install vue-awesome-swiper --save
  • Метод, используемый в компоненте, глобальное использование не имеет большого значения:
// 引入组件
import 'swiper/dist/css/swiper.css' 
import { swiper, swiperSlide } from 'vue-awesome-swiper'

// 在components中注册组件
components: {
    swiper,
    swiperSlide
}

// template中使用轮播
// ref是当前轮播
// callback是回调
// 更多参数用法,请参考文档
<swiper :options="swiperOption" ref="mySwiper" @someSwiperEvent="callback">            
    <!-- slides -->            
    <swiper-slide><div class="item">1</div></swiper-slide>            
    <swiper-slide><div class="item">2</div></swiper-slide>            
    <swiper-slide><div class="item">3</div></swiper-slide>            
          
    <!-- Optional controls -->            
    <div class="swiper-pagination"  slot="pagination"></div>            
    <div class="swiper-button-prev" slot="button-prev"></div>            
    <div class="swiper-button-next" slot="button-next"></div>            
    <div class="swiper-scrollbar"   slot="scrollbar"></div>
</swiper>

// 参数要写在data中
data() {            
    return {     
        // swiper轮播的参数           
        swiperOption: { 
            // 滚动条                   
            scrollbar: {                        
                el: '.swiper-scrollbar',                    
            }, 
            // 上一张,下一张                   
            navigation: {                        
                nextEl: '.swiper-button-next',                        
                prevEl: '.swiper-button-prev',                    
            },
            // 其他参数…………   
        }            
    }                    
},

Какие функциональные требования необходимо настроить для swiper, и вы можете добавить или удалить их согласно документации. Прикрепите документ:документация по нпм,swiper3.0/4.0 Документация, чтобы узнать больше об использовании, обратитесь к документации.


Проблема генерации большого .map файла после упаковки

После упаковки проекта код сжимается и шифруется.Если во время выполнения сообщается об ошибке, выходное сообщение об ошибке не может точно определить, где код сообщает об ошибке. Сгенерированный файл с суффиксом .map может быть похож на незашифрованный код, а точный вывод — это то, какая строка и какой столбец неверны.Вы можете настроить этот тип файла так, чтобы он не создавался. Но нам не нужны файлы .map в среде генерации, поэтому мы не можем сгенерировать эти файлы при упаковке:

В файле config/index.js установитеproductionSourceMap: false, вы не можете создать файл .map


Решение fastClick с задержкой 300 мс

При разработке мобильного проекта событие клика будет иметь задержку 300 мс. Что касается того, почему существует эта проблема, пожалуйста, сами Baidu. Здесь упоминаются только общие решения, будь то проект vue или проект jq, его можно использовать.fastClickрешить.

УстановитьfastClick:

cnpm install fastclick -S

Представлено в main.jsfastClickи инициализация:

import FastClick from 'fastclick'; // 引入插件
FastClick.attach(document.body); // 使用 fastclick


Порядок, в котором параметры записываются в компоненте

Почему опционы должны иметь единый порядок написания? Проще говоря, все дело в минимизации выбора и когнитивных затрат.

  1. побочный эффект(влияния вне триггерного компонента)

    • el
  2. глобальное осознание(Требуются знания, отличные от компонентов)

    • name
    • parent
  3. тип компонента(изменить тип компонента)

    • functional
  4. Модификатор шаблона(изменить способ компиляции шаблона)

    • delimiters
    • comments
  5. зависимости шаблона(Ресурсы, используемые в шаблоне)

    • components
    • directives
    • filters
  6. комбинация(объединить свойства в опции)

    • extends
    • mixins
  7. интерфейс(интерфейс компонента)

    • inheritAttrs
    • model
    • props/propsData
  8. местное государство(местные реактивные свойства)

    • data
    • computed
  9. мероприятие(обратные вызовы, вызванные реактивными событиями)

    • watch
    • Хуки жизненного цикла (в порядке их вызова)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeDestroy
      • destroyed
  10. не отвечающие свойства(не зависит от свойств экземпляра системы ответа)

    • methods
  11. оказывать(Декларативное описание вывода компонента)

    • template/render
    • renderError


Проверьте громкость каждого файла после упаковки, чтобы помочь вам быстро найти большие файлы

Если вы проект инициализирован Vue-CLI, он будет установлен по умолчаниюwebpack-bundle-analyzerПлагин, этот плагин может помочь нам просмотреть сравнение структуры тома проекта и всех зависимостей, используемых в проекте. Вы также можете визуально увидеть долю объема каждого модуля во всем проекте. Очень властный и древесный~~


npm run build --report // 直接运行,然后在浏览器打开http://127.0.0.1:8888/即可查看

Не забудьте поставитьnpm run devОткрыть локально закрыть


Ленивая загрузка маршрута (также называемая ленивой загрузкой)

Маршрутизация ленивой загрузки может помочь нам не загружать лишние ресурсы при входе на первый экран, тем самым снижая скорость загрузки первого экрана.

файл маршрута,

Неленивая загрузка записи:

import Index from '@/page/index/index';
export default new Router({  
    routes: [    
        { 
            path: '/', 
            name: 'Index',     
            component: Index 
        }
    ]
})
Метод записи маршрутизации с ленивой загрузкой:

export default new Router({
  routes: [    
        { 
            path: '/', 
            name: 'Index', 
            component: resolve => require(['@/view/index/index'], resolve) 
        }
   ]
})


Включить код сжатия gzip

Для одностраничного приложения, такого как спа, первый экран загружает все ресурсы одновременно, поэтому скорость загрузки всех первых экранов очень низкая. Один из очень эффективных способов решить эту проблему — открыть gizp на переднем и заднем концах (другие включают кэширование, маршрутизацию отложенной загрузки и т. д.). Gizp на самом деле помогает нам уменьшить размер файла, который можно сжать примерно до 30%, то есть файл размером 100 КБ составляет всего около 30 КБ после gizp.

В проекте, инициализируемом vue-cli, эта конфигурация предоставляется по умолчанию, и ее нужно только включить. Но сначала нужно установить плагин:

// 2.0的版本设置不一样,本文写作时为v1版本。v2需配合vue-cli3cnpm i compression-webpack-plugin@1.1.11 

Затем откройте его в config/index.js:

build: {
    // 其他代码
    …………
    productionGzip: true, // false不开启gizp,true开启
    // 其他代码
}

При упаковке теперь, в дополнение к предыдущим файлам, также генерируются gzip-файлы, заканчивающиеся на .gz. Конкретная реализация такова, что если клиент поддерживает gzip, то в фоновом режиме возвращается файл после gzip, а если не поддерживает, то возвращает обычный файл без gzip.

**Уведомление: здесь запакован gzip для внешнего интерфейса, но также требуется настройка фонового сервера. Конфигурация относительно проста, просто настройте несколько строк кода.Как правило, эта операция может быть выполнена братьями и сестрами по эксплуатации и обслуживанию.Если нет эксплуатации и обслуживания, пусть фон поможет конфигурации.


Практика возврата на страницу списка со страницы сведений для кэширования данных и позиции просмотра, а также других страниц, входящих на страницу списка для обновления данных.

Такой сценарий: есть три страницы: домашняя страница/страница поиска, страница классификации продуктов и страница сведений о продукте. Мы надеемся, что при входе на страницу категории с главной страницы, странице категории необходимо обновить данные.При входе на страницу сведений из категории и последующем возврате на страницу категории, мы не хотим обновляться.Мы надеемся, что страница категории в это время можно кэшировать загруженные данные и автоматически сохранять данные о пользователе в месте второго просмотра. В прошлом поиск Baidu в основном выполнялся с помощью keep-alive, но всегда были некоторые недостатки, поэтому после подведения итогов я выполнил следующую практику.

Чтобы решить это требование сценария, мы можем использовать свойство keepAlive, предоставляемое vue. Отправить другую статью прямо здесьПортал для решения этой проблемыБар


Скрытая область CSS и глубокие селекторы

Все знают, когда<style>Этикетка имеетscopedсвойство, его CSS действует только на элемент в текущем компоненте. Так как же он этого добился?Вы можете увидеть код до и после компиляции:

Перед компиляцией:

<style scoped>
.example {
  color: red;
}
</style>

После компиляции:

<style>
.example[data-v-f3f3eg9] {
  color: red;
}

Прочитав вас, вы поймете, собственно, в стиле компонента, который вы пишете, добавляя атрибут, так что достигается так называемая приватная область видимости. Но будут недостатки, учитывая, сколько селекторов CSS отображается, когдаp { color: red }Во много раз медленнее при ограничении области видимости (т. е. в сочетании с селекторами атрибутов). Если вместо этого вы используете class или id, например.example { color: red }, влияние на производительность устраняется. Итак, в своих стилях избегайте использования тегов напрямую, вместо этого вы можете дать тегам имя класса.


если хочешьscopedСелектор в стиле может идти «глубже», например, затрагивая дочерние компоненты, вы можете использовать>>>оператор:

<style scoped>
    .parent >>> .child { /* ... */ }
</style>

Приведенный выше код будет скомпилирован в:

.parent[data-v-f3f3eg9] .child { 
    /* ... */ 
}

Для прекомпиляций, таких как less или sass, не поддерживается.>>>Операторы, вы можете использовать/deep/заменить>>>оператор, например: .parent /deep/ .child { /* ... */ }


==================================

Буду продолжать обновлять позже:


  • Унифицированное управление инкапсуляцией axios и интерфейсом API (обновлено, по ссылке выше)
  • Гипер открытый тест скорости
  • Два способа получить данные vue + скелетный экран
  • Двусторонняя привязка данных пользовательских компонентов (родительско-дочерние компоненты)
  • Управление разделением маршрутизации
  • миксины упрощают общие операции
  • После упаковки файлы, изображения и ресурсы фонового изображения не существуют или указан неверный путь.
  • Разработка плагина Vue, публикация на github, настройка отображаемого адреса, публикация пакета npm

------------ Великолепная разделительная линия --------------- Великолепная разделительная линия--- -------------- -------- Великолепная разделительная линия -------- -- Великолепная разделительная линия ------------ ------------- Великолепная разделительная линия -------------- ------------ Великолепная разделительная линия ------ --------- Великолепная разделительная линия--- ------------

Hiper: восхитительный инструмент для анализа производительности


Как показано на рисунке выше, это результат теста инструмента hiper, из которого мы можем увидеть время, затраченное на DNS-запрос, время на TCP-соединение, время до достижения браузером первого байта, время на загрузку страницы, время. для продолжения загрузки ресурсов после DOM Ready, времени белого экрана, времени DOM Ready и общего времени загрузки страницы.

Установите глобально в нашем терминале редактора:

cnpm install hiper -g

использовать:Команда ввода терминала: URL теста hiper

# 当我们省略协议头时,默认会在url前添加`https://`

 # 最简单的用法
 hiper baidu.com

 # 如何url中含有任何参数,请使用双引号括起来
 hiper "baidu.com?a=1&b=2"

 #  加载指定页面100次
 hiper -n 100 "baidu.com?a=1&b=2"

 #  禁用缓存加载指定页面100次
 hiper -n 100 "baidu.com?a=1&b=2" --no-cache

 #  禁JavaScript加载指定页面100次
 hiper -n 100 "baidu.com?a=1&b=2" --no-javascript
 
 #  使用GUI形式加载指定页面100次
 hiper -n 100 "baidu.com?a=1&b=2" -H false

 #  使用指定useragent加载网页100次
 hiper -n 100 "baidu.com?a=1&b=2" -u "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"

В этом примере использования я скопировал документацию напрямую, подробности можно посмотреть в документации,Отправить ссылку сюда. Когда наш проект открывается медленно, этот инструмент может помочь нам быстро определить, какой шаг влияет на скорость загрузки страницы.

Обычно мы проверяем производительность, просматривая данные производительности и сети, записывая несколько ключевых показателей производительности, а затем несколько раз обновляя данные, чтобы увидеть эти показатели производительности. Иногда мы обнаруживаем, что из-за слишком малого количества образцов на это сильно влияет текущая загруженность «сети», «ЦП» и «памяти», а иногда оптимизированный проект работает медленнее, чем до оптимизации.

Если есть инструмент, который запрашивает страницы N раз за раз, а затем берет каждый индекс производительности и усредняет его, мы можем очень точно знать, является ли оптимизация «позитивной оптимизацией» или «негативной оптимизацией».

Hiper должен решить эту болевую точку.


Практика двух способов получения данных vue + реализация простого каркасного экрана

Есть два способа получить данные в Vue, Если вы представляете, это:

  • Получить после завершения навигации: сначала завершите навигацию, а затем извлеките данные в следующих обработчиках жизненного цикла компонента. Во время сбора данных отображается индикация типа «Загрузка».

  • Получить до завершения навигации: Перед завершением навигации получите данные от охранника, введенного по маршруту, и выполните навигацию после успешного получения данных.

С технической точки зрения оба подхода хороши — все зависит от того, какой пользовательский опыт вы хотите. Итак, давайте попрактикуемся в этих двух способах получения данных, а также немного подумаем об оптимизации взаимодействия с пользователем.

1. Первый есть первый: после завершения навигации,Этот метод используется большинством из нас (потому что, возможно, мы знаем этот метод только в начале ^V^). При использовании этого подхода мы перемещаемся и визуализируем компонент сразу, а затем в компонентеcreatedПолучить данные из хука. Это дает нам возможность отображать состояние загрузки во время выборки данных, а также отображать различные состояния загрузки между разными представлениями. Все знают, как получать данные. Вот некоторые сведения о пользовательском опыте:

  • Перед получением данных компоненты страницы были загружены, но данные не были получены и отображены, поэтому в этом процессе мы не можем загрузить компонент, который отображает данные на странице, но должен иметь загрузочный компонент или скелетный экран. .
  • При сбое сбора данных страницы можно понять, что по истечении времени ожидания запроса мы хотим отобразить компоненты, которые отключены от сети.
  • Если это страница списка, также рассмотрите ситуацию с пустыми данными, то есть с компонентом пустой подсказки.

Затем наша страница должна иметь эти три основные части, поместите код:

<template>
    <div class="list">
        <!--加载中或者骨架屏-->
        <div v-if="loading">
       
        </div>

        <!--请求失败,即断网的提示组件-->
        <div v-if="error">
      
        </div>

        <!--页面内容-->
        <div v-if="requestFinished" class="content">
            <!--页面内容-->
            <div v-if="!isEmpty">
                <!--例如有个列表,当然肯定还会有其他内容-->
                <ul></ul>
            </div>

            <!--为空提示组件-->
            <div v-else>空空如也</div>
        </div>
    </div>
</template>

В этом случае получения данных мы по умолчанию заходим для отображения содержимого экрана загрузки или скелета, а затем, если получение данных не удается (то есть время запроса истекло или сеть отключена), компонент с загружается ошибка, а другие компоненты скрыты. Если запрос данных успешен, компонент, который загружает контент, скрывает другие компоненты. Если это страница списка, в компоненте содержимого может быть список и пустая подсказка, поэтому в это время также необходимо решить, загружать ли содержимое или загружать пустую подсказку в соответствии с полученными данными.

Второй, второй способ: получить до завершения навигации

Этот способ находится на страницеbeforeRouteEnterДанные запрашиваются в хуке, и переход на страницу навигации произойдет только после успешного получения данных.

beforeRouteEnter (to, from, next) {        
    api.article.articleDetail(to.query.id).then(res=> {            
        next(vm => {                
            vm.info = res.data;                
            vm.loadFinish = true            
        })        
    })    
},

1. Все знают крючокbeforeRouteEnterЭто нельзя использовать в хуке, поэтому, если мы хотим выполнять операции присваивания или вызывать методы, мы можем обрабатывать их только в функции обратного вызова метода next().Первый параметр этой функции обратного вызова представляет это, которое будет инициализировано в компоненте. Сделайте это после успеха.

2. Я думаю, что много раз наши методы api или axios монтируются на прототипе vue, поскольку здесь это нельзя использовать, мы можем только внедрить api или наши axios в компонент страницы.

3. Операция присваивания также может быть записана в методе метода, но вызов этого метода присваивания по-прежнемуvm.yourFunction()Путь.

4. Пустые подсказки, обработка отключения и т. д. такие же, как и в первом способе, однако, поскольку сначала получаются данные, а затем загружается компонент, нам не нужно отображать скелетный экран или загружаемый компонент на ожидаемой странице. . Да, нам нужно иметь запрос на загрузку перед входом на текущую страницу, то есть, когда загружается предыдущая страница, например, индикатор выполнения в верхней части страницы. Таким образом, взаимодействие с пользователем становится более дружелюбным, и пользователь не знает, к чему приведет долгое отсутствие ответа из-за низкой скорости запроса. Глобальный индикатор выполнения в верхней части страницы может быть установлен router.beforeEach(to, from, next) {} в main.js. При изменении маршрута страницы отображается индикатор выполнения в верхней части страницы, и индикатор выполнения скрывается после ввода нового маршрута.


О том, как добавить индикатор выполнения, потому что это было написано в другой статье,Отправьте ссылку прямо сюда, больше никакой траты места. Операция тоже относительно простая, можете проверить сами.

Собственно говоря здесь, тоСкелетонный экранПроблема, кстати, решена.В общем случае экран скелета страницы представляет собой изображение скелета страницы, но следует отметить, что это изображение должно быть как можно меньше.


Двусторонняя привязка данных пользовательских компонентов (родительско-дочерние компоненты)

Когда дело доходит до связи между родительским и дочерним компонентами, все должны быть знакомы с этим: родительский компонент передает значения дочернему компоненту через свойства, а дочерний компонент запускает пользовательское событие родительского компонента через emit. Но здесь я хочу поговорить о взаимодействии между родительскими и дочерними компонентами с использованием v-модели. Я считаю, что когда вы используете чужие библиотеки компонентов, вы часто используете v-model для управления отображением и скрытыми эффектами компонента, такими как всплывающие окна. Давайте разгадаем тайну v-model шаг за шагом. Хватай~~ Спокойно~~ Эй~~, старый водитель вот-вот нажмет на педаль газа в углу~~~

Это то, что мы сначала думаем о V-модели, это то, что у нас есть простая и грубая работа с двунаправленными данными формы пользовательских данных, таких как:

<input type="text" v-model="msg">

data () {            
    return {                
        msg: ''            
    }        
}

На самом деле V-модель является синтаксическим сахаром. Приведенный выше код имеет тот же эффект, что и следующий код:

<input type="text" :value="msg" @input="msg = $event.target.value">
data () {
    return {
        msg: '' 
    }        
},

Отсюда видно, чтоv-model="msg"На самом деле это :value="msg" @input="msg = $event.target.value"синтаксический сахар. Это фактически мониторинг формы.inputсобытие, затем изменить:valueсоответствующее значение. Помимо использования v-model в формах ввода, его также можно использовать в компонентах,На официальном сайте об этом упоминаетсяТем не менее, он не очень подробно подробно, заставляя небольшой партнер, который только что связался, в облаке ощущение облака. С момента использования грамматической сахарной природы V-образца мы можем реализовать двустороннюю передачу данных отца и дочерних компонентов:

Способ реализации вышеуказанного принципа, способ записи 1:

Использование родительского компонента:

<empty v-model="msg"></empty>

Написание подкомпонента:

// 点击该按钮触发父子组件的数据同步
<div class="share-btn" @click="confirm">确定</div>

// 接收父组件传递的value值
// 注意,这种实现方法,这里只能使用value属性名
props: {            
    value: {                
        type: Boolean,                
        default: false            
    }        
},
methods: {            
    confirm () {                
        // 双向数据绑定父组件:value对应的值 
        // 通过$emit触发父组件input事件,第二个参数为传递给父组件的值,这里传递了一个false值 
        // 可以理解为最上面展示的@input="msg = $event.target.value"这个事件
        // 即触发父组件的input事件,并将传递的值‘false’赋值给msg             
        this.$emit('input', false)            
    }        
}

Этот метод реализует операцию двусторонней привязки данных родительского и дочернего компонентов, см. v-model.Например, можно попробовать реализовать работу компонента глобального всплывающего окна, и управлять отображением и скрытием всплывающего окна. -up окно через v-model, потому что это нужно делать в странице.Некоторые операции будут отображать его, а код для управления его сокрытием прописан в компоненте.Когда компонент скрыт, соответствующее значение родительского компонента также следует изменить.

Связь v-модели между родительским и дочерним компонентами, реализованная вышеописанным способом, осуществима, но она ограничивает значение имени атрибута, которое мы должны получить, и должен быть введен триггер отправки, что может привести к конфликту, особенно в форме. Таким образом, чтобы более элегантно использовать v-модель связи для разрешения конфликтов, мы можем использоватьmodelOption, ниже демонстрируется способ написания 2:

Написание родительского компонента:

<empty v-model="msg"></empty>

Написание подкомпонента:

<div class="share-btn" @click="confirm">确定</div>

// model选项用来避免冲突
// prop属性用来指定props属性中的哪个值用来接收父组件v-model传递的值
// 例如这里用props中的show来接收父组件传递的v-model值
// event:为了方便理解,可以简单理解为父组件@input的别名,从而避免冲突
// event的值对应了你emit时要提交的事件名,你可以叫aa,也可以叫bb,但是要命名要有意义哦!!!
model: {            
    prop: 'show',            
    event: 'changed'        
},
props: {
    // 由于model选项中的prop属性指定了,所以show接收的是父组件v-model传递的值            
    show: {                
        type: Boolean,                
        default: false            
    }        
},        
methods: {            
    confirm () {                
        // 双向数据绑定父组件传递的值
        // 第一个参数,对应model选项的event的值,你可以叫aa,bbb,ccc,起名随你 
        this.$emit('changed', false)            
    }        
}

Этот метод реализации компонентов «родитель-потомок», см. значение привязки v-модели, на самом деле очень распространен в нашей разработке, особенно когда вы хотите инкапсулировать общедоступные компоненты.

Наконец, на самом деле существуют способы реализации двусторонней привязки данных..sync, этот атрибут был там в начале, а позже был удален, потому что он считался или разрушал односторонний поток данных, но в конце концов было доказано, что он все еще имеет значение, поэтому он был добавлен обратно в версии 2.3.

Например: родительский компонент:

<empty :oneprop.sync="msg"></empty>

data () {
    return {
        msg: ''
    }
}

Сборка:

<div class="share-btn" @click="changeMsg">改变msg值</div>

props: {            
    oneprop: {                
        type: String,                
        default: 'hello world'
    }        
},        
methods: {            
    changeMsg () {                
        // 双向数据流
        this.$emit('update:msg', 'helow world')           
    }        
}        

Таким образом, данные родительского компонента могут быть обновлены в дочернем компоненте. из-заv-modelОн используется только один раз, поэтому, когда есть несколько значений, требующих двусторонней привязки,.syncЕсть еще несколько сценариев использования. .sync — это синтаксический сахар для следующего, предназначенный для упрощения наших операций:

<empty
    :msg="message"
    @update:msg="message = $event"
></empty>

После освоения написания компонентов v-модели будет легче инкапсулировать некоторые общие компоненты.

Снова здесь:

  • vm.$emit(event ,[...args])Основная функция этого API — запускать события в текущем экземпляре. Дополнительные параметры передаются обратному вызову слушателя. Дочерние компоненты также принадлежат текущему экземпляру. Первый параметр: имя запускаемого события. Последующие параметры являются необязательными: они передаются как параметры инициируемому событию.Документация
  • Слушайте пользовательские события в текущем экземпляре. События могут быть запущены с помощью $emit илиФункция крюка контролируется через крючок,

vm.$on(событие, обратный вызов): продолжайте слушать;Документация

vm.$once(событие, обратный вызов): прослушать один раз;Документация

vm.$off([event, callback]): удалить слушателя;Документация

Прослушайте пользовательское событие, вызванное $emit, которое использовалось выше, и прослушайте функцию ловушки, которая также продемонстрирована в разделе таймера выше. Сцена прослушивания функции хука используется мало, но знать ее все же необходимо.

  • vm.$attrs: вы можете получить все пользовательские атрибуты, передаваемые родительским компонентом, кроме класса и стиля.
  • vm.$listeners: вы можете получить все пользовательские события, переданные родительским компонентом

Например: родительский компонент:

<empty
    :msg="message"
    :title="articleTitle"
    @confirm="func1"
    @cancel="func2"
></empty>

Вы можете получить свойства и события, переданные родительским компонентом, в дочерний компонент, не определяя его в свойствах. Простая демонстрация подкомпонента выглядит следующим образом:

created() {            
    const msg = this.$attrs.msg; // 获取父组件传递的msg
    this.$listeners.confirm && this.$listeners.confirm(); //若组件传递事件confirm则执行
},

Это будет полезно, когда мы напишем некоторые продвинутые компоненты.


Управление разделением маршрута

Упомянутое здесь разделение маршрутизации относится к разделению файлов маршрутизации по модулям, что удобно для управления маршрутизацией и, что более важно, удобно для разработки несколькими людьми. Разбивать его или нет, зависит от ситуации в вашем проекте.Если проект небольшой, то есть всего десять или двадцать маршрутов, то разбиение совсем не нужно. Однако, если вы разрабатываете несколько проектов торговых центров с большим количеством функциональных точек, может быть сто или даже сотни маршрутов, поэтому в это время необходимо разделить файл маршрута. В противном случае, если вы посмотрите на длинный список маршрутов в файле index.js, это тоже очень плохо.


Сначала мы создаем index.js в папке маршрутизатора в качестве файла входа для маршрутизации, а затем создаем новую папку модулей, в которой хранятся файлы маршрутизации каждого модуля. Например, здесь хранится файл маршрутизации для модуля голосования Voting.js и файл маршрутизации для публичного модуля. Давайте перейдем непосредственно к index.js, а затем кратко представим:

import Vue from 'vue'
import Router from 'vue-router'

// 公共页面的路由文件
import PUBLIC from './modules/public' 
// 投票模块的路由文件
import VOTE from './modules/vote' 

Vue.use(Router)

// 定义路由
const router = new Router({  
    mode: 'history',  
    routes: [    
        ...PUBLIC,    
        ...VOTE,  
    ]
})

// 路由变化时
router.beforeEach((to, from, next) => {    
    if (document.title !== to.meta.title) {        
        document.title = to.meta.title;    
    }    
    next()
})

// 导出
export default router

Сначала введите vue и router и, наконец, экспортируйте, это не так много, основная операция.

положи сюдаrouter.beforeEachРабота роутера прописана в файле index.js роутера.Некоторые люди могут прописать это в файле main.js.В этом нет ничего плохого, но лично, так как это операция маршрутизации, то лучше ею управлять в файле маршрутизации. Вот, кстати, как автоматически модифицировать работу заголовка страницы при переключении страницы.

Затем импортируйте каждый файл js, который вы разделили по модулю маршрутизации, а затем при инстанцировании маршрута в массиве маршрутов вынесите импортированные файлы методом присвоения структуры. Конечный результат такой же, как при обычном письме.

Затем взгляните на импортированный файл voice.js:

/** 
 * 投票模块的router列表  
 */

export default [    
    // 投票模块首页    
    {        
        path: '/vote/index',        
        name: 'VoteIndex',        
        component: resolve => require(['@/view/vote/index'], resolve),        
        meta: {            
            title: '投票'        
        }    
    },    
    // 详情页    {        
    path: '/vote/detail',        
    name: 'VoteDetail',        
    component: resolve => require(['@/view/vote/detail'], resolve),
    meta: {            
        title: '投票详情'        
    }    
}] 

Здесь нужно экспортировать маршрутизацию модуля голосования в массив. Операция разделения всего маршрута — это не знание vue, а синтаксис импорта, экспорта и структуры es6. Разделять его или нет, зависит от проекта и среды.

Маршрутизация здесь использует метод маршрутизации с отложенной загрузкой, если что непонятно, то это вводится в тексте.

В мета-поле здесь определяется информация о заголовке для хранения заголовка текущей страницы, то есть document.title.

миксины упрощают общие операции

Мы часто сталкиваемся с двумя десятичными знаками для денег, преобразования временных меток и других операций в разработке. Каждый раз мы будем писать публичную функцию, а затем фильтровать в фильтрах на странице. Этот метод каждый раз, но я чувствую, что каждый раз, когда мне нужно его использовать, мне приходится снова прописывать его в фильтрах, что также больше раздражает! ! ! Однако конечная цель наших обезьян — лень, так как же это сделать~~~

Братцы, копируйте, ребята! Займитесь миксинами! ! !

import { u_fixed } from './tool'

const mixins = {    
    filters: {        
        // 保留两位小数        
        mixin_fixed2 (val) {            
            return u_fixed(val)        
        },
        // 数字转汉字,16000 => 1.60万        
        mixin_num2chinese (val) {            
            return val > 9999 ? u_fixed(val/10000) + '万' : val;        
    }    
}}
export default mixins

Создадим новый mixins.js и пропишем в него весь контент, который нам нужен для микширования.Например, здесь смешаны фильтры, и в нем прописано несколько часто используемых операций.Вы можете расширить его самостоятельно.

В таком случае импортируем этот js на нужную нам страницу, а затем объявляем миксин, и тогда мы можем его использовать в обычном режиме.


Например, теперь я могу использовать наше действие фильтра прямо на странице.{{1000 | mixin_fixed2}}


После упаковки файлы, изображения и ресурсы фонового изображения не существуют или указан неверный путь.

Сначала просмотрите файл index.js в папке конфигурации проекта. Этот параметр конфигурации сделает общедоступным путь к нашим упакованным ресурсам. Значение по умолчанию:‘/’, то есть корневой путь, поэтому путь упакованного ресурса является статическим в корневом каталоге. Вот в чем проблема.Если ваши упакованные ресурсы размещены не в корневом каталоге сервера, а в папках типа mobile в корневом каталоге, то упакованный путь будет конфликтовать с путем в вашем коде.Ресурс не может быть найден.

Итак, чтобы решить эту проблему, вы можете изменить указанный выше путь с корневого каталога «/» на‘./’относительный путь.


В этом случае путь запакованного образа, js и т.д.‘./static/img/asc.jpg’Такой относительный путь, куда ни поставь, ошибки не будет. Тем не менее, все есть, но ~~~~~~ здесь все нормально, но путь фонового изображения все еще неверен. Потому что в это время родственник становитсяstatic/css/в папкеstatic/img/xx.jpg, но на самом делеstatic/css/нет папкиstatic/img/xx.jpg,Прямо сейчасstatic/css/static/img/xx.jpgне существует. Путь относительно текущего файла css на данный момент. Итак, чтобы решить эту проблему, нам нужно добавить публичный путь к фоновому изображению в нашем css.‘../../’, то есть пусть он вернется на два уровня вверх иindex.htmlРасположение файла того же уровня, затем относительный путь в это времяstatic/img/xx.jpgсоответствующие ресурсы можно найти. Итак, как изменить публичный путь фонового изображения, потому что фоновое изображение анализируется загрузчиком, поэтому оно естественно модифицируется в конфигурации загрузчика, откройте файл utils в папке сборки, найдите функцию exports.cssLoaders. , и найдите соответствующую функцию в функции Следующие конфигурации:

Найдите это место, добавьте конфигурацию, которая является кодом в красном поле выше, вы можете изменить его общедоступный путь, чтобы вернуться на два уровня выше. Тогда упакуйте его и посмотрите, все в порядке!

Наконец, позвольте мне торжественно сказать, что если ваш режим маршрутизации является историей, то для его упаковки на сервере требуется сотрудничество фонового сервера.За подробностями вы можете обратиться к официальной документации, что очень важно. В противном случае вас ждет белый экран и другие необъяснимые проблемы. иметь ввиду! ! !


Разработка плагина Vue, публикация на github, настройка отображаемого адреса, публикация пакета npm

Для некоторых часто используемых компонентов мы можем инкапсулировать его как плагин, затем опубликовать его на github и, наконец, опубликовать в виде пакета npm, чтобы в будущем мы могли установить плагин прямо из npm в наш проект. избавляя нас от необходимости копировать процесс можно поделиться с другими!

Поскольку в этой части плагина много контента, я пока вынесу его в отдельную статью.Ссылка здесь.