用electron写个浏览器给自己玩

16,767 阅读3分钟

浏览器这种东西工程量很唬人,但是有了electron+webview我们就相当于只需要干组装的活就可以了,而且产品目标就是给自己玩, 成品的效果 image.png 😄本来想写成专业的技术博客,但是发现大家好像对那种密密麻麻,全是代码的技术博客不感兴趣,我就挑重点来写吧。 image.png

下载拦截功能

image.png

下载逻辑如果不做拦截处理的话,默认就是我们平常写web那种弹窗的方式,既然是浏览器肯定不能是那样的。 electron中可以监听BrowserWindow的页面下载事件,并把拿到的下载状态传给渲染线程,实现类似浏览器的下载器功能。

//这个global.WIN =   global.WIN = new BrowserWindow({ ...})
 global.WIN.webContents.session.on('will-download', (evt, item) => {
 //其他逻辑
       item.on('updated', (evt, state) => {
       //实时的下载进度传递给渲染线程
       })
 })

页面搜索功能

当时做这个功能的时候我就觉得完了,这个玩意看起来太麻烦了,还要有一个的功能这不是头皮发麻啊。

image.png 查资料和文档发现这个居然是webview内置的功能,瞬间压力小了很多,我们只需要出来ctrl+f的时候把搜索框弹出来这个UI就可以了,关键字变色和下一个都是内部已经实现好了的。

function toSearch() {
  let timer
  return () => {
    if (timer) {
      clearTimeout(timer)
    }

    timer = setTimeout(() => {
      if (keyword.value) {
        webviewRef.value.findInPage(keyword.value, { findNext: true })
      } else {
        webviewRef.value.stopFindInPage('clearSelection')
      }
    }, 200)
  }
}
  function closeSearch() {
  showSearch.value = false
  webviewRef.value.stopFindInPage('clearSelection')
}

function installFindPage(webview) {
  webviewRef.value = webview
  webviewRef.value.addEventListener('found-in-page', (e) => {
    current.value = e.result.activeMatchOrdinal
    total.value = e.result.matches
  })
}

当前标签页打开功能

就是因为chrome和edge这些浏览器每次使用的时候开非常多的标签,挤在一起,所以我想这个浏览器不能主动开标签,打开了一个标签后强制所有的标签都在当前标签覆盖。

app.on('web-contents-created', (event, contents) => {
  contents.setWindowOpenHandler((info) => {
    global.WIN?.webContents.send('webview-url-is-change')
    if (info.disposition === 'new-window') {
      return { action: 'allow' }
    } else {
      global.WIN?.webContents.send('webview-open-url', info.url)
      return { action: 'deny' }
    }
  })
})

渲染线程监听到webview-open-url后也就是tart="_blank"的情况,强制覆盖当前不打开新窗口

ipcRenderer.on('webview-open-url', (event, url) => {
  try {
    let reg = /http|https/g
    if (webviewRef.value && reg.test(url)) {
      webviewRef.value.src = url
    }
  } catch (err) {
    console.log(err)
  }
})

标签页切换功能

这里的切换是css的显示隐藏,借助了vue-router image.png

这里我们看dom就能清晰的看出来。

image.png

地址栏功能

地址栏支持输入url直接访问链接、支持关键字直接打开收藏的网站、还支持关键字搜索。优先级1打开收藏的网页 2访问网站 3关键字搜索

function toSearch(keyword) {
    if (`${keyword}`.length === 0) {
      return false
    }
    // app搜索
    if (`${keyword}`.length < 20) {
      let item = null
      const list = [...deskList.value, ...ALL_DATA]
      for (let i = 0; i < list.length; i++) {
        if (
          list[i].title.toUpperCase().search(keyword.toUpperCase()) !== -1 &&
          list[i].type !== 'mini-component'
        ) {
          item = list[i]
          break
        }
      }
      if (item) {
        goApp(item)
        return false
      }
    }

    // 网页访问
    let url
    if (isUrl(keyword)) {
      if (!/^https?:\/\//i.test(keyword)) {
        url = 'http://' + keyword
      } else {
        url = keyword
      }
      goAppNewTab(url)
      return false
    } else {
      // 关键字搜索
      let searchEngine = localStorage.getItem('searchEngine')
      searchEngine = searchEngine || CONFIG.searchEngine
      url = searchEngine + keyword

      if (!router.hasRoute('search')) {
        router.addRoute({
          name: 'search',
          path: '/search',
          meta: {
            title: '搜索',
            color: 'var(--app-icon-bg)',
            icon: 'search.svg',
            size: 1
          },
          component: WebView
        })
        keepAliveInclude.value.push('search')
      }

      router.push({
        path: '/search',
        query: { url }
      })

      setTimeout(() => {
        Bus.$emit('toSearch', url)
      }, 20)
    }
  }

桌面图标任意位置拖动

这个问题困扰了我很久,因为它不像电脑桌面大小是固定的,浏览器可以全屏也可以小窗口,如果最开始是大窗口然后拖成小窗口,那么图标就看不到了。后来想到我干脆给个中间区域固定大小,就可以解决这个问题了。因为固定大小出来起来就方便多了。这个桌面是上下两层

//背景格子
    <div v-show="typeActive === 'me'" class="bg-boxs">
      <div
        v-for="(item, i) in 224"  //这里有点不讲究了直接写死了
        :key="item"
        class="bg-box"
        @dragenter="enter($event, { x: (i % 14) + 1, y: Math.floor(i / 14) + 1 })"
        @dragover="over($event)"
        @dragleave="leave($event)"
        @drop="drop($event)"
      ></div>
    </div>
 // 桌面层
 // ...
import { ref, computed } from 'vue'
import useDesk from '@/store/deskList'
import { storeToRefs } from 'pinia'

export default function useDrag() {
  const dragging = ref(null)
  const currentTarget = ref()
  const desk = useDesk()

  const { deskList } = storeToRefs(desk)
  const { setDeskList, updateDeskData } = desk

  function start(e, item) {
    e.target.classList.add('dragging')
    e.dataTransfer.effectAllowed = 'move'
    dragging.value = item
    currentTarget.value = e
    console.log('开始')
  }
  let timer2
  function end(e) {
    dragging.value = null
    e.target.classList.remove('dragging')
    setDeskList(deskList.value)

    if (timer2) {
      clearTimeout(timer2)
    }
    timer2 = setTimeout(() => {
      updateDeskData()
    }, 2000)
  }
  function over(e) {
    e.preventDefault()
  }

  let timer
  function enter(e, item) {
    e.dataTransfer.effectAllowed = 'move'
    if (timer) {
      clearTimeout(timer)
    }

    timer = setTimeout(() => {
      if (item?.x) {
        dragging.value.x = item.x
        dragging.value.y = item.y
      }
    }, 100)
  }
  function leave(e) {}

  function drop(e) {
    e.preventDefault()
  }

  return { start, end, over, enter, leave, drop }
}

image.png

image.png

东西太多了就先介绍这些了

安装包地址

github.com/jddk/aweb-b…

也可以到官网后aweb123.com 如何进入微软商店下载,mac版本因为文件大于100mb没有传上去所以暂时还用不了。