[Цветочная закладка] Любимый URL-адрес небольшого проекта Vue3+TS

внешний интерфейс Vue.js
[Цветочная закладка] Любимый URL-адрес небольшого проекта Vue3+TS

написать впереди

Что такое Flowing Warbler Bookmark

«Цветущая камышевка» — это слово, которое мне очень нравится, изначально оно относилось к летающим и поющим птицам-камышевкам, так же как и я очень случайный человек. "Liuying Bookmark" — это веб-сайт, используемый для хранения избранных URL-адресов и управления ими унифицированным образом. Хотя сам браузер имеет собственную функцию избранного и может создавать несколько папок, я лично считаю, что его все еще очень трудно найти, и это долго. некрасиво. Поэтому я хотел сделать полезный и красивый фаворит под названием «Закладка Liuying».

Почему существует закладка Flowing Orioles

До запуска этого проекта компания использовалаVUE2Серия+JS, а я 👉👉собственный блогтакже на основеVUE2Да, вVUE3После выхода официальной версии, с одной стороны, компания обновилаVUE3Кроме того, я также хочу изучить больше техник, улучшить свои способности и конкурентоспособность, а такжеTSЯ видел это некоторое время, но не практиковал, поэтому мы просто вместе практиковали тренировку рук.

адрес проекта

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

Адрес источника

Это полностью открытый исходный код, и вы можете свободно исследовать и развивать его. Конечно, вы по-прежнему можете нажать на звездочку⭐⭐⭐
👉👉Ссылка на источник (gitee)👉👉Ссылка на источник (гитхаб)

Дисплей проекта

16.gif

каталог проекта

в основном стандартКаталог проектов строительных лесов, базовые компоненты и бизнес-компоненты хранятся отдельно, каждый компонент - папка, следующая папка - следующаяVUE文件,ОдинTS文件,TS文件В основном он хранит некоторые данные и объявления типов.

├── src 
     ├── assets      // 存放静态资源
     ├── baseComponents  // 基础组件
     │    └──Form    // 表单组件
     │    │    ├──Form.vue
     │    │    └──index.ts
     │    ├── Input    // 输入框组件
     │    │    ├──Input.vue
     │    │    └──index.ts   
         │      ....
     ├── components  // 业务组件
     │   ├── BookMark    // 页面主要内容相关组件
     │   │     ├──BookMark.vue
     │   │     ├──useLabels.ts
     │   │     └──index.ts   
     │   .....
     ├── hooks       // 封装的一些复用逻辑
     ├── styles      // 样式
     ├── utils       // 存放自己封装的工具
     ├── APP.vue
     └── main.ts

Особенности/особенности проекта

Функция

✅Работа с тегом

То есть классификация поддерживает операции добавления, удаления и изменения.

✅Операция закладки

То есть сохраненный URL-адрес поддерживает операции добавления, удаления и изменения.

✅Поиск

Вы можете ввести содержимое в поле ввода и щелкнуть соответствующий значок для поиска. В настоящее время поддерживаются Baidu, Google и Bing. Введите, чтобы использовать поиск Baidu по умолчанию.

✅Перевод

Нажмите значок перевода, чтобы быстро перевести, в настоящее время поддерживает Baidu

✅Экспорт, импорт конфигурации

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

характерная черта

💎локальное хранилище

Использование проектаlocalStorageХраните данные, поэтому не очищайте кеш по желанию, если вы не сделали резервную копию, иначе все ваши коллекции будут уничтожены.

💎 Получить автоматически

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

💎Основные компоненты

Проект не использует какую-либо библиотеку компонентов и сам инкапсулирует некоторые базовые компоненты, такие какDialog,Message,Input,FormЖдать

проект не используется

❎vue-router4, vuex4

vue3Экологичныйvue-router4,vuex4, но поскольку сам проект несложный, он не используется и может быть добавлен позже по мере расширения функции.

❎<настройка скрипта>

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

Основные моменты

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

Строительство проекта

👉👉Создайте проект Vite2+VUE3+TS с нуля

Упакованные базовые компоненты

👉👉Основные компоненты (1): Кнопка, Наложение, Диалог, Сообщение

👉👉Основные компоненты (2): Форма, Ввод

бизнес-компонент

👉👉Бизнес-компоненты, общая компоновка (нет)

импорт, экспорт конфигурации

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

//file.ts
import createMessage from 'base/Message/index';
// 文件下载
export const downloadFile = (jsonStr: any) => {
  // 将数据转换为字符串
  jsonStr = JSON.stringify(jsonStr);
  // 创建 blob 对象
  const blob = new Blob([jsonStr]);
  // 创建一个a标签
  const el = document.createElement('a');
  // 创建一个 URL 对象并传给 a 的 href
  el.href = URL.createObjectURL(blob);
  // 设置下载的默认文件名
  el.download = '流莺书签数据备份.json';
  // 模拟点击链接进行下载
  el.click();
};
// 文件上传
export const uploadFile = (e: any) => {
  return new Promise((reject) => {
    // 如果没有选择文件就什么也不做
    if (e.target.value === '' || e.target.files.length < 1) {
      return;
    }
    // 如果不是json格式的文件,给出提示
    if (e.target.files[0].type !== 'application/json') {
      createMessage('请上传由本站导出的json格式的文件 !');
      return;
    }
    // 创建 FileReader对象
    const reader = new FileReader();
    // 把文件读取为字符串
    reader.readAsText(e.target.files[0]);
    // 文件读取完成
    reader.onload = function (ev: any) {
      reject(ev);
    };
  });
};

Используется в компоненте, я скрываю загруженный файлinputИспользуйте значок для моделирования нажатия кнопки загрузки, после успешного чтения файла введитеpromiseизrejectстатус, а затем убедитесь, что загруженный файл соответствует формату. Если они не совпадают, выдается сообщение об ошибке, и данные заменяются, если они совпадают.

//这里是简略过的伪代码,详情请查阅源码
<template>
  <i class="iconfont" @click='handleClick(1)'></i>
  <i class="iconfont" @click='handleClick(2)'></i>
  <input type="file" id="file-select" @change="handleUploadFile">
</template>

<script lang='ts'>
//此处省略引用
export default defineComponent({
  setup(props, { emit }) {
    // 点击图标的处理函数
    const handleClick = (index:number) => {
      if (index === 1) {
        // 获取type为file的input元素
        const input = document.querySelector('#file-select') as HTMLInputElement;
        // 模拟点击
        input.click();
      }
      if (index === 2) {
        // 从浏览器本地取出数据
        const warblerData = getItem('WARBLER_DATA');
        const themeData = getItem('THEME_DATA');
        // 整合数据
        let jsonStr: any = {
          warblerData,
          themeData,
        };
        downloadFile(jsonStr);
      }
    };
    // 触发上传文件函数
    const handleUploadFile = (e: Event) => {
      uploadFile(e).then((ev: any) => {
        // 为什么要包裹一层try catch, 因为 JSON.parse在转换的时候,如果格式不符合要求会报错  如果报错说明上传的JSON不是我们想要的,给出提示即可
        try {
          // 把 JSON 字符串转换为 JSON 对象
          const jsonObj = JSON.parse(ev.target.result);
          // 验证JSON的格式是不是我们需要的格式
          const test = () => {
            // 标志变量
            let flag = true;
            // 循环我们需要的key  在读取的文件中判断是否具有我们所需要的所有key值  如果没有就返回错误
            dataFormat.forEach((dataItem) => {
              const result = Object.keys(jsonObj).find((jsonItem) => dataItem === jsonItem);
              if (!result) {
                flag = false;
              }
            });
            return flag;
          };
          // 如果格式不符合本站要求,给出提示
          if (!test()) {
            createMessage('请上传由本站导出的json格式的文件 !');
            return;
          }
          // 如果符合要求  触发更新数据方法
          emitter.emit('update-warblerData', jsonObj.warblerData);
        } catch (error) {
          createMessage('请上传由本站导出的json格式的文件 !');
        }
      });
    };
  },
});
</script>
//此处省略css,详情请查看源码

получить значок автоматически

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

мой блог основан наexpressДа, и он был развернут на моем собственном сервере, поэтому я прокатился и написал этот интерфейс в своем проекте блога.

/*
 * @Description:获取网站标题和图标的爬虫
 * 1.某些网站有大佬设计了反爬,我就是写了最基本的爬虫,根本进不去网站
 * 2.某些网站虽然能进去,但是图标经过了各种骚操作,我找不到
 *   所以前端支持自动获取失败的时候,手动选择图标
 * 3.错误码 300 没有填写网址  301请求失败
 * 4.请求失败 也会在error返回text字段 里面包含网站图标  只不过取不到网站内容
 *   我们不需要内容 只需要title和icon 所以我们在错误处理中也进行一次爬取
 */
// 用来发送请求的模块
const superagent = require('superagent');
// 用来托管html的模块
const cheerio = require('cheerio');

//获取网站主域名
const getFinallyUrl = (targetUrl) => {
  //  将目标域名以“//”进行分割
  const urlArray = targetUrl.split('//');
  //定义最终的域名
  let finallyUrl = '';
  //这个判断的意思是  如果数组存在第[1]项  那么证明这个网址是以http/https开头的  否则就是不带有http/https的
  if (urlArray[1]) {
    //如果带有http/https 咱们就把http/https给拼上返回
    finallyUrl = urlArray[0] + '//' + urlArray[1];
  } else {
    //如果不带有http/https 咱也不知道是http还是https  就返回原来的值
    finallyUrl = urlArray[0] + '/';
  }
  return finallyUrl;
};

//获取最终的图标地址
const getFinallyIcon = (finallyUrl, icon) => {
  let finallyIcon = '';
  if (icon) {
    //这个判断的意思是 如果存在://或者www. 则证明路径中是绝对路径  否则是相对路径
    if (icon.indexOf('//') > -1 || icon.indexOf('www.') > -1) {
      //如果是绝对路径直接使用
      finallyIcon = icon;
    } else {
      //如果是相对路径的话,就给拼接上当前网站的主域名
      finallyIcon = finallyUrl + icon;
    }
  }
  return finallyIcon;
};

//获取标题和图标地址
const getTitleAndIcon = (finallyUrl, text) => {
  //获取到的网页是本文格式,node自身无法解析,所以交给cheerio进行托管
  const $ = cheerio.load(text);
  //获取网站标题
  const title = $('title').text();
  //由于不同网站的icon格式不同,这里预设了几种
  //但是由于某些网站的大佬进行了各种包装,导致基本的爬取方式获取不到
  const icon1 = $("[rel='icon']").attr('href');
  const icon2 = $("[href$='.ico']").attr('href');
  const icon3 = $("[rel='shortcut icon']").attr('href');
  let icon = icon1 || icon2 || icon3;
  //获取最终图标的地址
  const finallyIcon = getFinallyIcon(finallyUrl, icon);
  return {
    title,
    finallyIcon,
  };
};

module.exports = async (req, res) => {
  //从请求体里获取将要爬取网站的url
  const targetUrl = req.body.targetUrl;
  //判断一下url是否为空,虽然前端也会校验
  if (!targetUrl) {
    return res.json({
      errorStatus: 300,
      errorMsg: '目标网址路径不可为空',
    });
  }
  const finallyUrl = getFinallyUrl(targetUrl);
  //模拟打开对应url的网页
  superagent
    .get(targetUrl)
    .then((superagentRes) => {
      //成功的话直接取text
      const { title, finallyIcon } = getTitleAndIcon(finallyUrl, superagentRes.text);
      //接口返回标题和图标地址
      res.json({
        title: title,
        icon: finallyIcon,
      });
    })
    .catch((error) => {
      console.log('🚀🚀~ error:', error);
      //错误处理
      //部分网站失败了也会有text字段  只不过取不到网站内容   我们不需要内容 只需要title和icon
      const { title, finallyIcon } = getTitleAndIcon(finallyUrl, error.response.text);
      res.json({
        errorStatus: 301,
        errorMsg: error.message,
        title: title,
        icon: finallyIcon,
      });
    });
};

следующий план развития

💡Поддерживает вкладки, сортировку закладок методом перетаскивания, а закладки можно перетаскивать на другие вкладки

💡Мобильная адаптация, теперь поддерживает только компьютерные браузеры

💡Измените тему, сам веб-сайт предоставляет два набора тем, темную и светлую, и поддерживает настройку

напиши в конце

Пожалуйста, не стесняйтесь просветить меня, прокомментируйте ниже или в личном сообщении, большое спасибо 🙏🙏🙏.

✅Считаю оформление какой-то части себя слишком громоздкой, и есть способы упаковки попроще или повыше

✅ думаю, что часть моего кода слишком старая, может предоставить новый API или последний синтаксис

✅Я не понимаю часть содержания статьи

✅Ответьте на несколько вопросов в моей статье

✅ Считать, что некоторые взаимодействия и функции нужно оптимизировать, и находить баги

✅Хотите добавить новые функции и получить лучшие предложения по общему дизайну и внешнему виду

Наконец, спасибо за ваше терпение в просмотре. Поскольку все это здесь, пожалуйста, нажмите 👍, как и идти.

Интеграция ссылок

🔊Адрес предварительного просмотра проекта (страницы GitHub): 👉👉alanhzw.github.io

🔊Дополнительный адрес предварительного просмотра проекта (собственный сервер): 👉👉warbler.duwanyu.com

🔊Исходный адрес (gitee): 👉👉git ee.com/One Piece_0174/Вау…

🔊Исходный адрес (github): 👉👉GitHub.com/Алан Ван Пис/война…

🔊Liuying Bookmark — создайте проект Vite+Vue3+Ts с нуля: 👉👉Наггетс.Талант/пост/695130…

🔊Цветочная закладка-Введение в основные компоненты (форма, ввод):👉👉Наггетс.Талант/пост/696393…

🔊Цветочная закладка-Основные компоненты (кнопка, оверлей, диалог, сообщение): 👉👉Наггетс.Талант/пост/696394…

🔊Цветочная закладка - перетащите, чтобы отсортировать: 👉👉Наггетс.Талант/пост/696864…

🔊Мой блог: 👉👉www.duwanyu.com