学习github的开源项目


项目介绍

特点

  • 移动端支持
  • DIY收藏网站
  • 界面简洁友好
  • 使用指南

技术栈

  • Figma
  • jQuery (BootCdn)
  • SCSS
  • parcel实时预览
  • @media媒体查询
  • IconFont( iconfont.cn)
  • SVG Symbols
  • JS 事件 阻止冒泡
  • LocalStorage
  • JSON 序列化
  • 哈希表
  • webpack配置
  • Git & GitHub
  • 短链处理
  • 二维码链接

后续更新

  • 实现 Bing + Google 搜索
  • 实现搜索 关键词 弹幕效果 Bullet Screen
  • vue版本
  • react版本

思路

  • Figma作图
  • 实现手机端
    • 写 HTML
    • 写 SCSS
    • 写 JS(事件监听、DOM 操作)
  • 实现PC端
    • 加 媒体查询 CSS
    • 写 JS(单独处理PC端逻辑)
  • 发布到 GitHub Gitee

实现过程

使用Figma作图

设计稿先导:https://dwz.cn/mnMSVtaC

  • 不自己配色,通用背景配色 #EEEEEE
  • 常用居中操作
  • 分组操作
  • Alt 平移复制
  • Shift 保持比例 变形
  • 使用 IconFont( iconfont.cn)的图标文字,下载 PNG 图片和生成链接插入网页
  • 复制 CSS 代码

手机页面 PC页面 对比参考 谷歌 Chrome 和 微软 Edge 的导航

自动标注功能可以用 摹客网

写HTML和SCSS

实时预览

  • parcel src/index.html

meta viewport

查看大厂的 meta viewport设置 进入chrome 开发者工具 > 移动端 > 点刷新 > 元素

  • index.html style.scss main.js

    1
    
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
  • 语义化标签 <header></header> <main></main>

  • 语义化样式类名 globalHeader globalMain site siteList lastLi

  • <form></form>表单 搜索框

  • 可以用a标签包裹div,实现点击链接

    1
    2
    3
    4
    
    <form class="searchForm" method="get" action="https://www.baidu.com/s" target="_blank">
      <input name="wd" type="text">
      <button type="submit">搜索</button>
    </form>

样式与布局

  • reset CSS
  • flex平均布局
  • justify-content align-items居中
  • 谨慎加高度,内部最小的加,撑开外部的; 用padding

使用 IconFont

  • IconFont( iconfont.cn)
  • 搜索关键词 plus
  • 添加项目
  • 查看在线链接
  • 选择Symbol
  • 插入生成代码 <script src="//at.alicdn.com/t/font_1765894_oi0jnamflr.js"></script>
  • 引入通用样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <style type="text/css">
    .icon {
      width: 1em;
      height: 1em;
      vertical-align: -0.15em;
      fill: currentColor;
      overflow: hidden;
    }
    </style>

图片

1
img {max-width: 100%;max-height: 100%;}

手机端测试

  • 查看 终端 ipconfig
  • 获取 IP ,加上端口号:1234 手机浏览器访问

jQuery

  • 引入 jQuery CDN 加速 BootCDN
  • 操作点击事件
  • 用户输入 url 合法性判断
  • 操作节点

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    const $li = $(`
    <li>
    <a href="${node.url}">
    <div class="site">
      <div class="logo">${node.logo[0]}</div>
      <div class="link">${simplifyUrl(node.url)}</div>
    </div>
    </a>
    </li>
    `).insertBefore($lastLi)
    

哈希表(数组)的应用

  • 把要添加的节点变为一种数据结构,存储,调用,渲染

main.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const hashMap = [{
  logo: 'A',
  logType: 'text',
  url: 'https://www.acfun.cn'
}, {
  logo: './images/bilib.png',
  logType: 'image',
  url: 'https://www.bilibili.com'
}, {
  logo: 'M',
  logType: 'text',
  url: 'https://developer.mozilla.org/zh-CN/'
}]// 注意用 parcel 时,默认在代码外加一层作用域 非全局变量 不用考虑全局污染

渲染函数

  • forEach遍历生成节点添加,渲染
  • 渲染 hashMap 之前必须先清空原来的$siteList.find('li:not(.last)').remove()

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    const render = () => {
    $siteList.find('li:not(.last)').remove()
    hashMap.forEach(node => {
    const $li = $(`
        <li>
          <a href="${node.url}">
            <div class="site">
              <div class="logo">${node.logo[0]}</div>
              <div class="link">${simplifyUrl(node.url)}</div>
            </div>
          </a>
        </li>
    `).insertBefore($lastLi)
    })
    }
    
  • 将所有对节点的操作,变为操作 hashMap 的 push 操作,渲染页面

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    /* 事件处理 */
    $('.addButton').on('click', () => {
    let url = window.prompt("请输入网址")
    /* 网址合法行判断 */
    if (url.indexOf("http") !== 0) {
    url = 'https://' + url
    }
    // console.log(url)
    hashMap.push({
    logo: url[0],
    logoType: 'test',
    url: url
    })
    render()
    }
    
  • 优化从第二个开始渲染 hashMap.slice(2)

LocalStorage 的应用

退出网站 用户关闭网站钱触发 存到 localStorage 里

  • 监听事件 window.onbeforeunload()

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    // 退出网站 用户关闭网站钱触发 存到 localStorage 里
    window.onbeforeunload = () => {
    console.log('页面要关闭了') // 可以开启 Preserve log 查看
    // 将 对象变为 字符串
    const string = JSON.stringify(hashMap)
    // console.log(hashMap)
    // console.log(typeof hashMap)
    // console.log(string)
    // console.log(typeof string)
    }
    

JSON 序列化

初始时

  • 在 开发者选项 Application 选项卡中可以看到
  • 从 LocalStorage 中读取 const x = localStorage.getItem('x')
  • 注意读取的是字符串,需要转变为对象
  • 将读取的字符串 变为 对象 const xObject = JSON.parse(x)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    const $siteList = $('.siteList')
    const $lastLi = $siteList.find('li.last')
    /* 初始时从  LocalStorage 中读取  */
    const x = localStorage.getItem('x')
    // 将读取的字符串 变为 对象
    const xObject = JSON.parse(x)
    // hashMap 初始化保底值
    const hashMap = xObject || [{
    logo: 'A',
    logType: 'text',
    url: 'https://www.acfun.cn'
    }, {
    logo: './images/bilib.png',
    logType: 'image',
    url: 'https://www.bilibili.com'
    }, {
    logo: 'M',
    logType: 'text',
    url: 'https://developer.mozilla.org/zh-CN/'
    }]
    

退出页面时

  • 退出页面时 保存 hashMap
  • 将 hashMap 对象变为 字符串const string = JSON.stringify(hashMap)
  • 注意 localStorage 要保存的是字符串,需要将对象 hashMap 转变为字符串
  • 保存到 localStorage.setItem('x', string)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    // 退出网站 用户关闭网站钱触发 存到 localStorage 里
    window.onbeforeunload = () => {
    // console.log('页面要关闭了') // 可以开启 Preserve log 查看
    // // 将 对象变为 字符串
    const string = JSON.stringify(hashMap)
    // console.log(typeof hashMap)
    // console.log(hashMap)
    // console.log(typeof string)
    // console.log(string)
    localStorage.setItem('x', string)
    }
    

其他注意

  • 清除 cookie 时 localstorage 就删除了
  • 或存储硬盘满了,也可能会删除 localstorage
  • 或使用无痕窗口

其他优化

  • 缩短显示链接
  • 处理 logo
  • 简化 URL
  • 删除功能
  • PC 网页(媒体查询)
  • PC 网页样式优化
  • PC 样式影响了手机样式
  • 键盘事件

缩短显示链接

1
2
3
const simplifyUrl = (url) => {
  return url.replace('https://', '').replace('http://', '').replace('www.', '')
}

简化 URL

1
2
3
4
5
6
7
/* 显示链接 缩短 */
const simplifyUrl = (url) => {
  return url.replace('https://', '')
      .replace('http://', '')
      .replace('www.', '')
}
/* <div class="link">${simplifyUrl(node.url)}</div> */

显示恰当的首字母

  • 先去掉 logo 显示图片,直接显示字母
  • 简化 去掉 logoType
  • 删掉 localStorage 存储内容
  • 调用simplifyUrl(url)[0]
  • 大写simplifyUrl(url)[0].toUpperCase()

    1
    2
    3
    4
    
    hashMap.push({
    logo: simplifyUrl(url)[0].toUpperCase(),
    url: url
    })
    
  • 大写也可以用 CSS 来控制 text-transform: uppercase;

显示 bug 输入长网址时 超出显示

再简化 URL

用正则表达式,表示以/开头的字符

删除功能

  • IconFont( iconfont.cn)
  • 搜索关键词 close 选择一个图标
  • 添加项目
  • 更新在线链接
  • 选择Symbol
  • 更新代码 <script src="//at.alicdn.com/t/font_1774301_yiq4rsp0i6q.js"></script>
  • 样式

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    hashMap.forEach(node => {
    const $li = $(`
        <li>
          <a href="${node.url}">
            <div class="site">
              <div class="logo">${node.logo}</div>
              <div class="link">${simplifyUrl(node.url)}</div>
              <div class="close">
                <svg class="icon">
                  <use xlink:href="#icon-close"></use>
                </svg>
              </div>
            </div>
          </a>
        </li>
    `).insertBefore($lastLi)
    })
    
    /*
    .close{
    position: absolute;
    right: 10px;
    top: 5px;
    }
    */
    
  • 关闭图标在a标签里,点击穿透,需要阻止冒泡

  • 监听对 close 图标的点击事件

    1
    2
    3
    4
    
    $li.on('click','.close',(e)=>{
    console.log('阻止冒泡')
    e.stopPropagation()
    })
    

点击close图标,还是跳转了

  • 去掉 a 标签, 用 JS 监听点击 li 标签

    1
    2
    3
    
    $li.on('click',()=>{
    window.open(node.url, '_self')
    })
    

webStorm 技巧 在变量后输入 .log,按 Tab健,变为console.log()这个变量

  • 在 hashMap 中找到表示点击当前网站的节点,然后删掉
  • 需要索引 index
  • forEach() 的第二个参数为 索引 index

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    hashMap.forEach((node, index) => {
    const $li = $(`
        <li>
          <div class="site">
            <div class="logo">${node.logo}</div>
            <div class="link">${simplifyUrl(node.url)}</div>
            <div class="close">
              <svg class="icon">
                <use xlink:href="#icon-close"></use>
              </svg>
            </div>
          </div>
        </li>
    `).insertBefore($lastLi)
    $li.on('click',()=>{
      window.open(node.url,'_self')
    })
    $li.on('click','.close',(e)=>{
      // console.log('阻止冒泡')
      e.stopPropagation()
      // console.log(hashMap);
      // console.log(index);
      hashMap.splice(index,1)
      render() // 删除后 重新渲染
    })
    })
    

渲染函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* 操作 hashMap 渲染页面 */
const render = () => {
  /*  再次渲染 hashMap 之前必须先 清空原来的 */
  // 找到 的除了 lastLi 之外的所有 li
  $siteList.find('li:not(.last)').remove()
  hashMap.forEach((node, index) => {
    const $li = $(`
        <li>
          <div class="site">
            <div class="logo">${node.logo}</div>
            <div class="link">${simplifyUrl(node.url)}</div>
            <div class="close">
              <svg class="icon">
                <use xlink:href="#icon-close"></use>
              </svg>
            </div>
          </div>
        </li>
  `).insertBefore($lastLi)
    $li.on('click',()=>{
      window.open(node.url,'_self')
    })
    $li.on('click','.close',(e)=>{
      // console.log('阻止冒泡')
      e.stopPropagation()
      // console.log(hashMap);
      // console.log(index);
      hashMap.splice(index,1)
      render()
    })
  })
}
  • 大部分时间处理不恰当的操作:用JS操作代替 a 标签 点击跳转、阻止冒泡
  • 剩下的时间才实现最终的逻辑

PC 网页(媒体查询)

  • 业界常识:PC页面的宽度是固定的
  • 优先用 max-width
  • 当元素有最大/小宽度(固定)时,就可以用margin:0 auto;来居中,但会覆盖其他元素上下margin
  • margin-left:auto; margin-right:auto;
  • 平均布局用负 margin

PC 网页样式优化

  • 通过改变visibility属性来显示/隐藏关闭按钮,再通过opacity属性的改变达到渐变的动画效果

键盘事件

  • 在 document 上绑定键盘事件

将Google嵌入iframe


发布到 GitHub

默认路径有问题

parcel build src/index.html --no-minify

换成 ./

  • 不用最小压缩,否则报错

parcel build src/index.html --no-minify --public-url ./

  • 如果要构建多个

parcel build src/index.html src/search.html --no-minify --public-url ./

yarn build 一键发布

再次build的时,只需用yarn init -y 创建package.json

  • package.json中加一段脚本

    1
    2
    3
    
    "scripts": {
    "build":"rm -rf dist && parcel build src/index.html --no-minify --public-url ./"
    },
    

再次 yarn build


原则和要点

原则

  • 没有移动端设计稿,绝对不要做移动端页面
  • 定一个小目标来实现(短链处理)
  • 没完美,做到看不出Bug

要点

  • 专业的前端工程师应该能快速的抄袭大厂网页里的 meta viewport
  • 前端工程师可以 flex 布局一把梭,解决 80% 以上的需求
  • 将对象变成一个字符串 JSON.stringify
  • 将字符串变成一个对象 JSON.parse
  • 一般来说 localStorage 里的数据用户清除浏览器数据的时候消失

优秀导航网页


· 未完待续 ·

参考文章

相关文章