IdbI

备忘录

HTML

1. HTML语义化标签

  • 语义化标签是一种写 HTML 标签的方法论/方式。
  • 实现方法是遇到标题就用 h1 到 h6,遇到段落用 p,遇到文章用 article,主要内容用 main,边栏用 aside,导航用 nav……(就是找到中文对应的英文)
  • 明确了 HTML 的书写规范
  • 一、适合搜索引擎检索;二、适合人类阅读,利于团队维护。

2. HTML5的新标签

  • 文章相关:header main footer nav section article figure mark
  • 多媒体相关:video audio svg canvas
  • 表单相关:input> type=email type=tel

3. Canvas 和 SVG 的区别

  • Canvas 主要是用笔刷来绘制 2D 图形的。
  • SVG 主要是用标签来绘制不规则矢量图的。
  • 相同点:都是主要用来画 2D 图形的。
  • 不同点:Canvas 画的是位图,SVG 画的是矢量图。
  • 不同点:SVG 节点过多时渲染慢,Canvas 性能更好一点,但写起来更复杂。
  • 不同点:SVG 支持分层和事件,Canvas 不支持,但是可以用库实现。

CSS

1. BFC 是什么

  • 块级格式化上下文
  • 可以清除浮动(为什么不用 .clearfix 呢?)
  • 可以防止 margin 合并
  • 哪些可以触发BFC
    • 浮动元素(元素的 float 不是 none)
    • 绝对定位元素(元素的 position 为 absolute 或 fixed)
    • 行内块 inline block 元素
    • overflow 值不为 visible 的块元素
    • 弹性元素(display为 flex 或 inline-flex元素的直接子元素)

2. 如何实现垂直居中

  • flex布局
    display: flex;
    justify-content: center;
    align-items: center;
    
  • absolute margin:auto
    width: 300px;
    height: 200px;
    position: absolute;
    margin: auto;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    
  • absolute translate(-50%, -50%);
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    
  • absolute margin: -50%;
    width: 300px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -150px;
    margin-top: -50px;
    
  • table
    <table class="parent">
        <tr>
        <td class="child">
        一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
        </td>
        </tr>
    </table>
    

3. CSS 选择器优先级如何确定?

  • 选择器越具体,其优先级越高
  • 相同优先级,出现在后面的,覆盖前面的
  • 属性后面加 !important 的优先级最高,但是要少用

4. 如何清除浮动?

  • 给父元素加上 .clearfix
    .clearfix:after{
      content: '';
      display: block; /*或者 table*/
      clear: both;
    }
    .clearfix{
        zoom: 1; /* IE 兼容*/
    }
    
  • 给父元素加上 overflow:hidden

5. 文字两端对齐

div {
  text-align: justify;
  overflow: hidden;
}
div::after {
  content: '';
  display: inline-block;
  width: 100%;
}

6. 文本溢出隐藏

/* 单行 */
div {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 多行 */
div {
  overflow: hidden; /* 文本超出的部分隐藏 */
  display: -webkit-box;/* 弹性伸缩盒模型显示 */
  -webkit-box-orient: vertical;/* 设置伸缩盒子对象的子元素的排列方式 */
  -webkit-line-clamp: 2;/* 限制在一个块元素中显示文本的行数 */
}

7. 两种盒模型(box-sizing)的区别?

  • 第一种盒模型是 content-box,即 width 指定的是 content 区域宽度,而不是实际宽度,公式为: 【实际宽度 = width + padding + border】
  • 第二种盒模型是 border-box,即 width 指定的是左右边框外侧的距离,公式为:【实际宽度 = width】

JavaScript

1. JS 的数据类型

  1. string
  2. number
  3. boolean
  4. null
  5. undefined
  6. object
  7. bigint
  8. symbol

2. 原型链是什么 参考文章

  1. a ===> Array.prototype ===> Object.prototype

  2. 创建原型链

    const x = Object.create(原型)
    // 或
    const x = new 构造函数() // 会导致 x.__?????__ === 构造函数.prototype
    
  3. this指向 参考文章

  4. JS 的 new 做了什么 参考文章

    • 创建临时对象/新对象
    • 绑定原型
    • 指定 this = 临时对象
    • 执行构造函数
    • 返回临时对象
  5. JS 的立即执行函数是什么 声明一个匿名函数,然后立即执行它。这种做法就是立即执行函数。 在 ES6 之前,只能通过它来「创建局部作用域」。

    (function(){alert('我是匿名函数')} ())  // 用括号把整个表达式包起来
    (function(){alert('我是匿名函数')}) ()  // 用括号把函数包起来
    !function(){alert('我是匿名函数')}()    // 求反,我们不在意值是多少,只想通过语法检查。
    +function(){alert('我是匿名函数')}()
    -function(){alert('我是匿名函数')}()
    ~function(){alert('我是匿名函数')}()
    void function(){alert('我是匿名函数')}()
    new function(){alert('我是匿名函数')}()
    var x = function(){return '我是匿名函数'}()
    
  6. JS 的闭包是什么?怎么用?

    • 闭包是 JS 的一种语法特性。
    • 闭包 = 函数 + 自由变量 解决了什么问题:
    • 避免污染全局环境。(因为用的是局部变量)
    • 提供对局部变量的间接访问。
    • 维持变量,使其不被垃圾回收。
  7. JS 如何实现类

    • 使用原型
      function Dog(name){ 
          this.name = name
          this.legsNumber = 4
      }
      Dog.prototype.kind = '狗'
      Dog.prototype.say = function(){
          console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
      }
      Dog.prototype.run = function(){
          console.log(`${this.legsNumber}条腿跑起来。`)
      }
      const d1 = new Dog('啸天') // Dog 函数就是一个类
      d1.say()
      
    • 使用 class
      class Dog {
          kind = '狗' // 等价于在 constructor 里写 this.kind = '狗'
          constructor(name) {
              this.name = name
              this.legsNumber = 4
              // 思考:kind 放在哪,放在哪都无法实现上面的一样的效果
          }
          say(){
              console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
          }
          run(){
              console.log(`${this.legsNumber}条腿跑起来。`)
          }
      }
      const d1 = new Dog('啸天')
      d1.say()
      
  8. JS 如何实现继承

    • 使用原型链
    function Animal(legsNumber){
    this.legsNumber = legsNumber
    }
    Animal.prototype.kind = '动物'
    
    function Dog(name){ 
    this.name = name
    Animal.call(this, 4) // 关键代码1
    }
    Dog.prototype.__proto__ = Animal.prototype // 关键代码2,但这句代码被禁用了,怎么办
    
    Dog.prototype.kind = '狗'
    Dog.prototype.say = function(){
    console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
    }
    
    const d1 = new Dog('啸天') // Dog 函数就是一个类
    console.dir(d1)
    
    var f = function(){ }
    f.prototype = Animal.prototype
    Dog.prototype = new f()
    
    • 使用 class
    class Animal{
    constructor(legsNumber){
        this.legsNumber = legsNumber
    }
    run(){}
    }
    class Dog extends Animal{
    constructor(name) {
        super(4)
        this.name = name
    }
    say(){
        console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
    }
    }
    
  9. 手写节流 throttle、防抖 debounce

    • 节流
        // 节流就是「技能冷却中」
        const throttle = (fn, time) => {
            let 冷却中 = false
            return (...args) => {
                if(冷却中) return
                fn.call(undefined, ...args)
                冷却中 = true
                setTimeout(()=>{
                    冷却中 = false
                }, time)
            }
        }
        // 还有一个版本是在冷却结束时调用 fn
        // 简洁版,删掉冷却中变量,直接使用 timer 代替
        const throttle = (f, time) => {
            let timer = null
            return (...args) => {
                if(timer) {return}
                f.call(undefined, ...args)
                timer = setTimeout(()=>{
                    timer = null
                }, time)
            }
        }
    

    使用方式:

    const f = throttle(()=>{console.log('hi')}, 3000)
    f() // 打印 hi
    f() // 技能冷却中
    
    • 防抖
    // 防抖就是「回城被打断」
    const debounce = (fn, time) => {
    let 回城计时器 = null
    return (...args)=>{
        if(回城计时器 !== null) {
        clearTimeout(回城计时器) // 打断回城
        }
        // 重新回城
        回城计时器 = setTimeout(()=>{
        fn.call(undefined, ...args) // 回城后调用 fn
        回城计时器 = null
        }, time)
    }
    }
    
  10. 手写发布订阅

    const eventHub = {
        map: {
            // click: [f1 , f2]
        },
        on: (name, fn)=>{
            eventHub.map[name] = eventHub.map[name] || []
            eventHub.map[name].push(fn)
        },
        emit: (name, data)=>{
            const q = eventHub.map[name]
            if(!q) return
            q.map(f => f.call(null, data))
            return undefined
        },  
        off: (name, fn)=>{
            const q = eventHub.map[name]
            if(!q){ return }
            const index = q.indexOf(fn)
            if(index < 0) { return }
            q.splice(index, 1)
        }
    }
    
    eventHub.on('click', console.log)
    eventHub.on('click', console.error)
    
    setTimeout(()=>{
        eventHub.emit('click', 'frank')
    },3000)
    
    class EventHub {
        map = {}
        on(name, fn) {
            this.map[name] = this.map[name] || []
            this.map[name].push(fn)
        }
        emit(name, data) {
            const fnList = this.map[name] || []
            fnList.forEach(fn => fn.call(undefined, data))
        }
        off(name, fn) {
            const fnList = this.map[name] || []
            const index = fnList.indexOf(fn)
            if(index < 0) return
            fnList.splice(index, 1)
        }
    }
    // 使用
    const e = new EventHub()
    e.on('click', (name)=>{
        console.log('hi '+ name)
    })
    e.on('click', (name)=>{
        console.log('hello '+ name)
    })
    setTimeout(()=>{
        e.emit('click', 'frank')
    },3000)
    
  • 手写 AJAX
const ajax = (method, url, data, success, fail) => {
  var request = new XMLHttpRequest()
  request.open(method, url);
  request.onreadystatechange = function () {
    if(request.readyState === 4) {
      if(request.status >= 200 && request.status < 300 || request.status === 304) {
        success(request)
      }else{
        fail(request)
      }
    }
  };
  request.send();
}
  • 手写简化版 Promise
class Promise2 {
  #status = 'pending'
  constructor(fn){
    this.q = []
    const resolve = (data)=>{
      this.#status = 'fulfilled'
      const f1f2 = this.q.shift()
      if(!f1f2 || !f1f2[0]) return
      const x = f1f2[0].call(undefined, data)
      if(x instanceof Promise2) {
        x.then((data)=>{
          resolve(data)
        }, (reason)=>{
          reject(reason)
        })
      }else {
        resolve(x)
      }
    }
    const reject = (reason)=>{
      this.#status = 'rejected'
      const f1f2 = this.q.shift()
      if(!f1f2 || !f1f2[1]) return
      const x = f1f2[1].call(undefined, reason)
      if(x instanceof Promise2){
        x.then((data)=>{
          resolve(data)
        }, (reason)=>{
          reject(reason)
        })
      }else{
        resolve(x)
      }
    }
    fn.call(undefined, resolve, reject)
  }
  then(f1, f2){
    this.q.push([f1, f2])
  }
}

const p = new Promise2(function(resolve, reject){
  setTimeout(function(){
    reject('出错')
  },3000)
})

p.then( (data)=>{console.log(data)}, (r)=>{console.error(r)} )
  1. 手写 Promise.all
  • 要在 Promise 上写而不是在原型上写
  • all 的参数(Promise 数组)和返回值(新 Promise 对象)
  • 用数组来记录结果
  • 只要有一个 reject 就整体 reject
Promise.prototype.myAll
Promise.myAll = function(list){
  const results = []
  let count = 0
  return new Promise((resolve,reject) =>{
    list.map((item, index)=> {
      item.then(result=>{
          results[index] = result
          count += 1
          if (count >= list.length) { resolve(results)}
      }, reason => reject(reason) )
    })
  })
}
  1. 前端如何优雅通知用户刷新页面
  • 轮询html Etag/Last-Modified
const oldHtmlEtag = ref();
const timer = ref();
const getHtmlEtag = async () => {
  const { protocol, host } = window.location;
  const res = await fetch(`${protocol}//${host}`, {
    headers: {
      "Cache-Control": "no-cache",
    },
  });
  return res.headers.get("Etag");
};

oldHtmlEtag.value = await getHtmlEtag();
clearInterval(timer.value);
timer.value = setInterval(async () => {
const newHtmlEtag = await getHtmlEtag();
console.log("---new---", newHtmlEtag);
if (newHtmlEtag !== oldHtmlEtag.value) {
    Modal.destroyAll();
    Modal.confirm({
    title: "检测到新版本,是否更新?",
    content: "新版本内容:",
    okText: "更新",
    cancelText: "取消",
    onOk: () => {
        window.location.reload();
    },
    });
}
}, 30000);

  • 自定义plugin,项目根目录创建/plugins/vitePluginCheckVersion.ts
import path from "path";
import fs from "fs";
export function checkVersion(version: string) {
  return {
    name: "vite-plugin-check-version",
    buildStart() {
      const now = new Date().getTime();
      const version = {
        version: now,
      };
      const versionPath = path.join(__dirname, "../public/versionData.json");
      fs.writeFileSync(versionPath, JSON.stringify(version), "utf8", (err) => {
        if (err) {
          console.log("写入失败");
        } else {
          console.log("写入成功");
        }
      });
    },
  };
}

在vite.config.ts中引入插件

import { checkVersion } from "./plugins/vitePluginCheckVersion";
plugins: [
  vue(),
  checkVersion(),
]

在App.vue中添加如下代码

const timer = ref()
const checkUpdate = async () => {
  let res = await fetch('/versionData.json', {
    headers: {
      'Cache-Control': 'no-cache',
    },
  }).then((r) => r.json())
  if (!localStorage.getItem('demo_version')) {
    localStorage.setItem('demo_version', res.version)
  } else {
    if (res.version !== localStorage.getItem('demo_version')) {
      localStorage.setItem('demo_version', res.version)
      Modal.confirm({
        title: '检测到新版本,是否更新?',
        content: '新版本内容:' + res.content,
        okText: '更新',
        cancelText: '取消',
        onOk: () => {
          window.location.reload()
        },
      })
    }
  }
}

onMounted(()=>{
  clearInterval(timer.value)
  timer.value = setInterval(async () => {
   checkUpdate()
  }, 30000)
})
  • 使用现有vite插件 @plugin-web-update-notification/vite
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { webUpdateNotice } from '@plugin-web-update-notification/vite'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    webUpdateNotice({
      logVersion: true,
    }),
  ]
})
返回上级