Имя Му Эр, мой апплет "Чжиху" (1)

внешний интерфейс JavaScript Апплет WeChat

предисловие

Я не знаю, сколько людей влюбляются в "Чжиху", как я? Мне всегда нравился эффект пользовательского опыта и некоторая новая передача информации, которую обеспечивает «Zhihu». Таким образом, в недавнем обучении по небольшой программе первым начатым проектным обучением является «Чжиху».
После того, как я начал это делать, я понял, что объем разработки апплета «Жиху» настолько велик, что я, новичок во фронтенде, не знаю, сколько рабочих дней нужно, чтобы сделать «Жиху» полным апплет. Однако путь программиста заключается в том, чтобы расти во время обучения, а обучению нет конца. Злодей не талантлив.Я кое-что сделал в последнее время.Я не мог не начать волну обмена опытом и проблемами.

Апплет "Жиху" запущен

Обмен достижениями

1. Главная

1. Переключение панели вкладок

Главная страница состоит из трех вкладок: «Подписаться», «Рекомендуемые» и «Горячий список». Функции переключения этих страниц настроены на «щелчок для переключения» и «пролистывание для переключения»:

Когда я впервые начал делать событие клика, у меня был простой лоб, простой и грубый, я напрямую устанавливал индекс данных для трех вкладок, а затем оценивал значение индекса, полученное каждым триггером события, а затем переходил к соответствующему содержимому. , "if...elseif...else...", что делать, если вкладок много? Если... какой год и месяц~ О Боже! Я чувствую, что я слишком низкий. Я всегда чувствую, что это замечательно — писать лучшие функции с самым коротким кодом. Ну, я все же попробовал:

home.js部分代码:
switchTab (e) {//tab点击事件
    let index = parseInt(e.target.dataset.index)
    this.setData({
      currentIndex: index
    })
  },
  handleChangeTab (e) {//tab滑屏事件
    const p = 33.3
    this.setData({
      lineStyle: `left: ${p * e.detail.current}%`
    })
  }

Непосредственно присваивайте значение индекса parseInt значению индекса текущей вкладки каждый раз, когда вы переходите к: currentIndex, сравнение числовых значений намного проще, чем сравнение строк, не так ли? Тогда результат события клика будет автоматически упоминаться в событии скользящего экрана. На этот раз я должен снова похвалить parseInt. Мне просто нужно автоматически умножать «выбранную строку» на текущий currentIndex каждый раз, когда событие запускается для получить его Позиция значений на разных текущих вкладках.
Правильно, это бесполезно, если... иначе! Я устала от этого, но, к счастью, у меня еще есть возможность изменить свой вкус. на этот раз я должен утешать себя тем, что я «иду по низовым», чтобы испытать жизнь ххххх

2. Проблема размещения изображения

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

решить:
wx:if => условный рендеринг
Добавьте элемент wx:if, страница автоматически определит, отображать ли блок кода, содержащийся в элементе, wx:if: "{{bound data}}", отображать, когда связанные данные имеют значение, а не когда есть нет значения.

3. Горячий список: простой макет с несколькими столбцами

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

home.wxml部分代码:
<view class="container-item" wx:for="{{hotList}}" wx:key="{{item.id}}">
          <view class="row">
            <view class="col">
              <view class="col-1">{{item.rank}}</view>
              <view class="col-7">
                <view class="title">{{item.title}}</view>
                <view class="status">{{item.status}}</view>
              </view>
              <view class="col-4">
               <image wx:if="{{item.titleImage}}" class="title-image" src="{{item.titleImage}}"></image>
              </view>
            </view>
         </view>
       </view>

Конечно, у апплета пока нет фиксированного значения для номера столбца, поэтому установка имени класса напрямую не изменится, и нет возможности использовать его напрямую, как с помощью API, поэтому каждый столбец определяется в глобальный стиль app.wxss Ширина блока:

.col>.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12{
  overflow: hidden;
}
.col-1{
  width: 8.33333333333333%;
}
.col-2{
  width: 16.66666666666666%;
}
.col-3{
  width: 25%;
}
.col-4{
  width: 33.33333333333333%;
}
.col-5{
  width: 41.66666666666666%;
}
.col-6{
  width: 50%;
}
.col-7{
  width: 58.33333333333333%;
}
.col-8{
  width: 66.66666666666666%;
}
.col-9{
  width: 75%;
}
.col-10{
  width: 83.33333333333333%;
}
.col-11{
  width: 91.66666666666666%;
}
.col-12{
  width: 100%;
}

Таким образом, если вы хотите снова построить этот макет в более поздней программе, вы можете напрямую и легко ссылаться на него.
Конечно, на этой странице, прежде чем установить это, реализация гибкой верстки обязательна. «Гибкая верстка» действительно моя любимая вещь в изучении CSS в этот период времени. Вы можете увидеть связанные знанияСтатья Руан Ифэн.

2. Страница "Идеи"

1. Исправление строки заголовка и функция исправления прокрутки

Этот интерфейс завершает ☝️ пользовательский опыт, который, по моему личному мнению, лучше при смахивании Zhihu: когда головная навигация перемещается на определенное расстояние, она по-прежнему фиксирует свою основную «точку передачи функций» в голове, если пользователь перемещается вниз для сравнения Когда вы хотите перейти к навигации головы, когда у вас большое расстояние, вам не нужно повторно скользить на большое расстояние вверх, чтобы выполнить это действие.
Давайте сначала посмотрим на эффект:

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

элемент конфигурации эффект
scroll-top Установите вертикальное положение полосы прокрутки
scroll-y Разрешить вертикальную прокрутку
bindscroll Функция обратного вызова срабатывает при прокрутке

Я использую событие прокрутки здесь, чтобы переключаться между двумя небольшими страницами (то есть перед прокруткой страницы и прокручивать на определенное расстояние, чтобы отобразить различные верхние панели навигационных навигаций TOP NAV, отображаемые по городу), с рамками «WX: IF SOUND» От апплета для условий рендеринга, переключите вновь сгенерированный верхний NAV, когда страница прокручивается до заданного целевого расстояния.

thought.wxml部分代码:
切换前的nav:
<view wx:if="{{!display}}" class="nav">
      <view class="title">想法</view>
      <view class="message">
        <image class="messageImg" src="{{messageImg}}" wx:if="{{messageImg}}"></image>
        <text class="messageTitle" >消息</text>
      </view>
      <view class="myThought">
        <image class="thoughtImg" src="{{thoughtImg}}" wx:if="{{thoughtImg}}"></image>
        <text class="thoughtTitle">我的想法</text>
      </view>
    </view>
切换后的nav:
<view wx:else class="nav1">
        <view class="title">想法</view>
        <view class="message">
          <image class="messageImg" src="{{messageImg}}" wx:if="{{messageImg}}"></image>
        </view>
        <view class="myThought">
          <image class="thoughtImg" src="{{thoughtImg}}" wx:if="{{thoughtImg}}"></image>
        </view>
    </view>

Установите разные стили для двух навигации соответственно.

Событие прокрутки запускает навигационный переход:
Событие Scroll получает два параметра:
1.верхняя прокрутка;
2. свойство отображения

scroll: function(e){
    if (e.detail.scrollTop > 200) {
      this.setData({
        display: true
      })
    } else {
      this.setData({
        display: false
      })
    }
  }

В этом месте есть несколько ям, на которые я наступил, когда начинал:
1. При использовании вертикальной полосы прокрутки для компонента должна быть установлена ​​фиксированная высота, иначе событие bindscroll не сработает;
2. Когда scroll-top устанавливает вертикальное положение полосы прокрутки, если заданное значение не меняется, компонент не будет отображаться!

Такая вот функция, по словам нашего учителя... Да, очень "фактурная"!

2. Контент карусели

Только те, кто писал карусель на h5, знают, что это относительно хлопотно.Карусельного компонента нет, а ViewPage тоже нужно настраивать.Компонент swiper в апплете относительно удобно инкапсулировать, да и способ его использования тоже очень удобно относительно просто.

Основные атрибуты:

элемент конфигурации эффект
indicator-dots Отображать ли точки индикатора панели
autoplay Переключаться ли автоматически
current индекс текущей страницы
interval Интервал времени автоматического переключения
duration Продолжительность анимации скольжения
bindchange Инициировать событие изменения при изменении текущего

Необходимо только установить атрибуты.Вы также можете извлекать данные в файл js для привязки данных, отслеживать и использовать bindchange, а также выполнять бизнес-обработку в js.
Блок кода wxml:

<swiper class="swiper" autoplay="true" interval="2000" duration="1000" circular="true" >
        <block wx:for="{{discussion}}" wx:for-index="index">  
              <swiper-item class="discuss">
                <image wx:if="{{item.url}}" src="{{item.url}}" class="swiper-url"/>
                <view class="discuss-desc">
                  <view class="discussing">{{item.discussing}}</view>
                  <view class="desc-title">{{item.title}}</view>
                  <view class="desc-question"> {{item.descQuestion}}
                    <!-- <swiper class="desc-question" autoplay="true" interval="3000" duration="1000" vertical="true">
                      <block wx:for="{{item.descQuestion}}" wx:if="{{item.descQuestion}}">
                        <swiper-item class="question">
                          <view class="question-item">{{item.questionItem}}</view>
                        </swiper-item>
                      </block>
                    </swiper> -->
                  </view> 
                </view>
              </swiper-item>
          </block>
        </swiper>

На самом деле в карусели на этой странице вложена и вертикальная карусель одного из элементов.Сначала я прямо в свайпер вложил еще один свайпер, а потом поставил verticle="true", но только внешний свайпер Карусель время переключения увеличено, чтобы зарезервировать время для отображения вертикальной карусели внутри, поэтому эффект опыта не очень хороший, если вы знаете, как это сделать, вы можете научить его следующему (๑`・ᴗ・´๑)ヾ( ●´∇ `●)ノВау~.

3. Страница поиска

1. «Введите, чтобы подтвердить» контент поиска

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

Здесь используется событие bindconfirm компонента ввода, то есть событие, которое срабатывает каждый раз, когда вводится содержимое поиска и нажимается Enter для подтверждения, здесь я использую цикл wx:for для настройки массива historyRecord, который получает два параметры, идентификатор каждого входного содержимого. Значение и содержимое recordItem, идентификатор значения ключа различает записи истории разных строк и разного содержимого, recordItem — это значение записи, выводимое на панели истории поиска:

search.wxml

<view class="search-history">
            <text class="zhhs">搜索历史</text>
            <view class="search-history-item" wx:for="{{historyRecord}}" wx:key="item.id">
              <image class="search-history-icon" src="/assets/icons/shizhong.png"></image>
              <text>{{item.recordItem}}</text>
            </view>
          </view>

В процессе обработки события bindconfirm я в самом начале зашел в тупик.Я сделал ошибку, когда впервые начал изучать небольшие программы.Все мы должны знать, что для изменения данных в данных мы можем использовать только setData.Сначала я использовал this.data.historyRecord.push({id:'',recordItem:e.detail.value}); непосредственно снаружи Потом я сообщил об ошибке мол нет метода push, а потом конечно же пошел к Ду Ниангу спросить внятно, мне подсказали, что данные в данных можно изменить только с помощью setData, и тут я стукнул головой, и наконец это выглядело следующим образом:

search.is

bindconfirm: function(e){
    console.log(e);
    var historyRecord = this.data.historyRecord;
    var recordItem = e.detail.value;
    historyRecord.unshift({
      id:'0',
      recordItem: recordItem
    });
    this.setData({
      historyRecord:historyRecord
    });
  }

Чтобы использовать методы, отличные от SetData, вам нужно занимать переменные для назначения значений. Если вы назначаете массив на внешнюю переменную, вы можете использовать методы, отличные от SetData.
А сама переменная — это массив, методов у массива столько, хватит! Сначала я использовал метод push(), а потом зашел на страницу поиска Zhihu и обнаружил, что последние записи истории прямо вставляются в первую строку, ну а метод массива unshift() вас удовлетворяет!

2. Контент поиска «Нажмите, чтобы найти результаты».

Каждый раз, когда вы вводите содержание поиска, будет появляться страница панели результатов поиска.Щелкните панель результатов, чтобы перейти на страницу сведений, и в панель истории поиска будет добавлена ​​новая запись.

Здесь используются нечеткий запрос и запрос по ключевым словам, а для обхода базы данных используется метод фильтра массива (здесь я временно использую поддельные данные, и я добавлю их после того, как освою внутренний метод), выберите соответствующие результаты, содержащие введите слова и перечислите их в разделе «Результаты» для выбора.

Search.wxml部分代码
<view wx:else class="search-like">
      <view class="search-like-item" data-param="{{item.text}}" wx:for="{{searchLikeList}}" wx:key="{{index}}" bindtap="turnTo">
          <image class="search-like-icon" src="/assets/icons/sousuo.png"></image>
          <text>{{item.text}}</text>
          <image data-index="{{index}}" class="turn" src="/assets/icons/turn.png"></image>
      </view>
    </view>

Search.js部分代码
changeSearch (e) {
    let value = e.detail.value
    if (value === '') {
      this.setData({
        haveSerachLike: false
      })
      return
    }
    let arr = this.data.searchLikeAllList.filter(item => item.text.indexOf(value) > -1)
    console.log(arr)
    this.setData({
      haveSerachLike: true,
      searchLikeList: arr,
    })
  },
turnTo: function(e){
    this.saveHistory({
      id: 0,
      recordItem: e.target.dataset.param
    })
    wx.navigateTo({
      url: '../searchDetail/searchDetail'
    })
  },

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

3. Прозрачная история поиска

Изображение эффекта:

Теперь, когда есть функция сохранения записей, конечно же, незаменима и функция очистки записей поиска.
Я чувствую, что держусь за бедро метода массива. Здесь я снова использую фильтр фильтра. Во-первых, идентифицируйте значение, которое будет передано, установив метод индекса данных для элемента, которому будет присвоено событие очистки, а затем в событии deleteRecord запись истории удаляется по значению индекса, выбранному событием, фильтруется, то есть удаляется переданное значение индекса, а затем возвращается отфильтрованный массив, то есть записи, которые не были отфильтрованы.

Search.wxml部分代码:
<view class="search-history">
          <text class="zhhs">搜索历史</text>
          <view class="search-history-item" wx:for="{{historyRecord}}" wx:key="{{index}}">
            <image class="search-history-icon" src="/assets/icons/shizhong.png"></image>
            <text>{{item.recordItem}}</text>
            <image data-index="{{index}}" class="delete" src="/assets/icons/delete.png" bindtap="deleteRecord"></image>
          </view>

search.js部分代码:
deleteRecord: function(e){
    console.log(e);
    let filterArr = this.data.historyRecord.filter((item, index) => {
      return index !== e.target.dataset.index
    })

    this.setData({
      historyRecord: filterArr
    })

    wx.setStorage({
      key: 'historyRecord',
      data: filterArr
    })
  },

4. Горячие поисковые запросы

Изображение эффекта:

В модуле популярных поисковых слов на странице поиска перечислены самые последние популярные поисковые запросы, а первым является самый популярный поисковый запрос, отсортированный по популярности.
(1) Макет:
Метод компоновки использует гибкую компоновку, горячие поисковые слова сортируются по горизонтали, а когда задана фиксированная ширина в процентах, flex-wrap: wrap; в гибкой компоновке используется для выполнения операции разрыва строки.
(2) реализация функции:
Добавьте параметр hotstatus к связанным горячим данным поискового слова, чтобы указать горячий статус, а тип — номер:

<view class="search-item">
                <view class="hot-search-item" wx:for="{{hots}}" wx:key="{{item.id}}">
                  <view class="hot-item">
                    <view class="text">
                      <image class="hot-img" src="{{item.hotImg}}" wx:if="{{item.hotImg}}"></image>
                      <text>{{item.text}}</text>
                    </view>
                    <view class="hot-status" >{{hotStatus}}</view>
                  </view>
                </view>         
            </view>

Затем добавьте метод сортировки к событию загрузки страницы onload, извлеките каждое значение hotstatus в массиве горячих слов и расположите его в блоке «Горячий поиск Zhihu» в порядке убывания:

onLoad: function (options) {
    var hots = this.data.hots;
    var hots2 = hots.sort((x, y) => y.hotStatus - x.hotStatus);
    // reverse()方法会反转数组项的顺序
    // hots.reverse();
    console.log(hots2);
    this.setData({
      hots: hots2
    })

5. Оптимизация кода

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

saveHistory (param) {
    let arr = this.data.historyRecord
    arr.unshift(param)
    wx.setStorage({
      key: 'historyRecord',
      data: arr
    })
    this.setData({
      historyRecord: arr
    })
  }

Немедленно функция события bindconfirm в первом пункте выше становится такой:

bindconfirm: function(e){
    console.log(e);
    var recordItem = e.detail.value;
        this.saveHistory({
      id: 0,
      recordItem
    })
turnTo事件函数:
turnTo: function(e){
    this.saveHistory({
      id: 0,
      recordItem: e.target.dataset.param
    })
    wx.navigateTo({
      url: '../searchDetail/searchDetail'
    })
  },

Код стал более лаконичным? Логика яснее? Это то, к чему мы стремились: «Написать лучшие функции с помощью кратчайшего кода!»

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

При разработке нативных приложений чаще используются функции pull-to-refresh и pull-up.
При разработке апплета апплет предоставляет только интерфейс обновления по запросу.
Bug & Tip:

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

Здесь я напрямую использую прокрутку, чтобы добиться обновления выпадающего меню и подтягивания, чтобы загрузить больше, у прокрутки есть три события события:

элемент конфигурации эффект
bindscrolltoupper Функция обратного вызова, активируемая прокруткой вверх
bindscrolltolower Прокрутите вниз, чтобы активировать функцию обратного вызова
bindscroll Функция обратного вызова срабатывает при прокрутке

Код js здесь на самом деле является логикой обработки:
Подтягивание: нам нужно соединить данные и номер страницы запроса на обработку после массива container-list;
Во-первых, вам нужно инкапсулировать метод получения данных кода страницы.

getPage:
getPage: function(){
    var that = this;
    var pageIndex = that.data.currentPage;
    wx.request({
      url: '',
      data: {
        page: pageIndex
      },
      success: function(res){
        if(pageIndex != 1){ // 加载更多
          console.log('加载更多');
          var tempArray = that.data.articles;
          tempArray = tempArray.concat(that.data.articles);
          that.setData({
            allPages: that.data.allPages,
            articles: tempArray,
            hideBottom: true
          })
        }
      }

Затем определите, является ли текущая страница последней страницей:

if (that.data.currentPage == that.data.allPages){
      that.setData({
        loadMoreData: '已经到顶'
      })
      return;
    }

Здесь добавлен таймер, чтобы продлить время отображения всплывающих и раскрывающихся представлений:

setTimeout(function(){
      console.log('上拉加载更多');
      var currentPage = that.data.currentPage;
      currentPage = currentPage + 1;
      that.setData({
        currentPage: currentPage,
        hideBottom: false  
      })
      that.getPage();  
    },300);

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

refresh: function(event){
    var that =  this;
    page = 1;  
    that.setData({  
      articles: [{这里加入传入的刷新的数据}]   
      scrollTop: 0,
      hidden:true  
    });  
    that.getPage();
    // GetList(this) 
  },

Вот волна готовых картинок:

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

Заключение:

Поскольку время относительно короткое, Zhihu также является большим проектом. Страницы и функции все еще нуждаются в постепенном улучшении. В выпущенных функциях также есть некоторые области, которые необходимо улучшить. В будущем этот проект будет медленно внедряться, и технология будет медленно полироваться. Код любви, любовь Zhihu! Добро пожаловать к таким же друзьям-единомышленникам по коду для получения дополнительных советов и обменов.ヾ(❀╹◡╹)ノ~

Кстати, адрес моего проекта прилагается:Имитация апплета WeChat "Zhihu"
значок «Спонсорство»:icon