console.log()

占位符 %o 它接受对象,%s 接受字符串,%d 表示小数或整数,%c CSS 值的占位符,对应的后面的参数必须是 CSS 语句

如:

console.log('%s 的价格是 %d 磅 %d 便士', '衬衫', 9, 15)

输出:衬衫的价格是 9 磅 15 便士

如:

console.log('I am a %cbutton', 'color: white; background-color: orange; padding: 2px 5px; border-radius: 2px')

输出:I am a button

console.dir()

console.warn()

输出警告信息,字的颜色是黄色的,用于区分或过滤掉无用的输出

console.table()

将数据以表格的形式输出,对于数据列表的输出更容易观察其数据结构,并且点击输出的表头可以对数据排序

第二个可选参数是需要输出的字段。默认输出所有字段

console.table(data) // 输出全部
console.table(data, ['id', 'price']) // 输出 data 中的 id price 字段

console.count()

计数器,可以用来统计代码被执行的次数

for (let i = 0; i < 5; i++) {
if (i % 2) {
console.count('odds')
}
}

输出结果为:

odds: 1
odds: 2
console.countReset() // 可以使用它重置计数器。

console.time()

是一个用于跟踪操作时间的专用函数,可以用来跟踪 js 的执行时间

const a = () => {
console.time('a')
// do something
console.timeEnd('a')
}
console.time()

for (i = 0; i < 10; ++i) {
a()
}
console.timeEnd()

选择 DOM 元素 $ 和 $$

  • 在谷歌开发控制台中可以使用 $('选择器') 类似于 jquery 的方式选择 DOM 元素

$(selector) 等效于 document.querySelector(selector)
$$(selector) 等效于 document.querySelectorAll(selector)

  • inspect($('selector')) 将检查与选择器匹配的元素,并转到 Elements 选项卡
  • $0$1$2 等可以获取最近检查过的元素,即通过 $0 即可直接获取当前高亮的元素,$1 可以获取上一个高亮的元素

将页面转换为可编辑状态

在 Console 中输入: document.body.contentEditable = true

之后页面中的内容即为可编辑状态,可以编辑 DOM 中的任何内容

查找与 DOM 中的元素关联的事件

getEventListeners($('selector')) 返回一个对象,其中包含绑定到该元素的所有事件

或 控制台 => Element => EventListeners

监控事件

监视绑定到 DOM 中特定元素的事件,然后在它们被触发后立即将它们记录在控制台中

// 监控绑定到 '#box' 元素的所有事件
monitorEvents($('#box'))

// 监控绑定到 'selector' 元素的 click 事件
monitorEvents($('#box'), 'click')

// 监控绑定到 'selector' 元素的 click 和 focus 事件
monitorEvents($('#box'), ['click', 'focus'])

// 停止监视
unmonitorEvents($('#box'))

检索最后一个结果的值

$_ 表示控制台中最近一次返回的值

'Hellow World'.split(' ')
$_.reverse()
$_.join(' ') // 'World Hellow'

断点调试

  1. 跳到下个断点, 如果后面没有断点了,那么代码直接执行完
  2. 单步调试 : 下一步 没有断点的话,函数就直接跳过
  3. 单步调试 : 进入函数
  4. 单步调试 : 跳出函数
  5. 单步调试 : 下一步 不管有没有断点,都会一步一步的走,纯碎的下一步
  6. 让所有的断点失效
  7. 自动根据错误断点



一键重新发起请求

在与后端联调接口时比较方便,不用刷新页面,不用走页面交互

控制台 - Network - 选择要重新发送的请求 - 右键选择Replay XHR(只有xhr请求才有)

在控制台修改入参重新发起请求

同样方便与后端联调接口

控制台 - Network - 选择要重新发送的请求 - 右键 Copy - Copy as fetch - 控制台粘贴代码, 修改参数回车

复制js变量

鼠标放在一个变量上,右键 Copy Object/String…

或 在控制台中使用 copy() 函数,将对象作为入参执行即可

将节点或变量保存为全局变量

鼠标放在一个Element节点或变量上,右键 Store xxx as global variable,在Console中将可以使用该变量

网页截图

可以截取指定节点或全部网页内容

使用前先打开控制台

截取全部网页内容:ctrl/cmd + shift + p 执行Command命令,输入Capture full size screenshot 按下回车

截取部分网页内容:先在 Elements 中选中要截取的节点,ctrl + shift + p 执行Command命令,输入Capture node screenshot 回车

展开所有DOM元素

调试元素时,在层级比较深的情况下,你是不是也经常一个个展开去调试?有一种更加快捷的方式

Alt/Opt + click(需要展开的最外层元素)

实时表达式

点击Console面板中的 小眼睛,输入一个 js 表达式,回车,该表达式将会在Console面板里置顶,并动态刷新表达式的值

awesome-project

项目集合

  • awesome
  • awesome-nodejs
  • awesome-cross-platform-nodejs
  • awesome-selfhosted
  • Awesome-Windows
  • awesome-python
  • Awesome-Hacking

知识库/教程

  • Daily-Interview-Question - 每天一道大厂前端面试题
  • CS-Notes - 技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计
  • typescript-tutorial - TypeScript 入门教程
  • mostly-adequate-guide - 函数式编程(英文)
  • mostly-adequate-guide-chinese - mostly-adequate-guide 中文翻译
  • typescript-book - (英文) TypeScript 的权威指南,可能是最好的 TypeScript 书籍
  • typescript-book-chinese - typescript-book 中文翻译
  • TypeScript - TypeScript 手册(官方手册中文翻译版)
  • CS-Interview-Knowledge-Map - 面试地图。内容包括JS、网络、浏览器相关、性能优化、安全、框架、- Git、数据结构、算法等。在线阅读
  • Vue.js 技术揭秘

js库(Browser/Nodejs)

  • formidable - 用于解析 form data 的 Node.js 模块,尤其是文件上传(Node.js)
  • multer - 用于处理 multipart/form-data 的 node.js 中间件,主要用于上传文件(Node.js)
  • form-data - 创建可读的 ‘multipart/form-data’ 流的 Node.js 模块,可以用来向其他web应用程序提交表单和文件上传(Node.js)
  • file-type - 检测 Buffer/Uint8Array/ArrayBuffer 的文件类型(Browser/Node.js)
  • cheerio - 服务器端解析任何HTML或XML文档,与jQuery有相似的语法(Node.js)
  • jsdom - 各种 Web 标准的 JavaScript 实现,用于Node.js中模拟web浏览器(Node.js)
  • marked - markdown 解析器(Browser)
  • showdown - markdown 解析器(Browser/Node.js)
  • node-emoji - 对 node.js 项目的简单表情符号支持 在node.js 项目中使用emoji,如在CLI项目中(Node.js)
  • node-semver - npm 包版本比较(Node.js)
  • leven - 测量两个字符串之间的差异
  • node-notifier - 使用 Node.js 发送跨平台原生通知,适用于 macOS、Linux、Windows(Node.js)
  • dom-to-image - 使用 canvas 从 DOM 节点生成图像
  • floating-vue - 用于 Vue 的简单工具提示、弹出框、下拉菜单、菜单..
  • vue-analytics - 适用于 Vue 的 Google Analytics 插件
  • Dexie.js - indexedDB 库
  • single-spa - 微前端
  • path-to-regexp - 将 /user/:name 等路径字符串转换为正则表达式(node.js)
  • Draft.js - 富文本编辑器(React)
  • nodebestpractices - Node.js 最佳实践列表
  • json-server - 根据 JSON 文件提供数据接口
  • decimal.js - 解决js运算精度问题
  • async
  • ora
  • web-vitals

工具

  • ohmyzsh - Oh My Zsh 是一个社区驱动的开源框架,用于管理 Zsh 配置
  • gitignore - 提供很多 .gitignore 模板
  • carbon - 将源码转化为漂亮的图片
  • nginxconfig - Nginx 可视化配置神器
  • Cyberbrain - python代码调试神器
  • codelf - 一个帮助开发者解决变量命名烦恼的工具
  • you-get - 一行命令下载全网视频
  • youtube-dl - 下载全网视频
  • lux - 视频下载神器(支持爱优腾等)
  • deskreen - 将任何设备转换为电脑的辅助屏幕

测试

  • Puppeteer - 无头chrome
  • Playwright - 无头Chromium, WebKit, and Firefox

沙雕项目

  • ChineseBQB - 中国表情包大集合
  • genact - 该神器会显示一些多任务场景,让看到你电脑屏幕的人都误以为你在 Coding
  • VSCode-Zhihu - 用 VSCode 看知乎的摸鱼神器
  • vscode-plugin-swimming - 写完代码之后可以重新把代码写一遍,摸鱼神器
  • preserve-cd - 绝版游戏保护计划:把一些经典小游戏刻录永久保存

效率工具

  • github1s - 网页版VS Code查看github项目代码
  • watchtower - 自动更新 Docker 容器

机器人

  • mirai - 高效率QQ机器人支持库
  • go-cqhttp - cqhttp的golang实现,轻量、原生跨平台

模拟器

  • Ryujinx - PC 上 Switch 模拟器

api

  • public-apis - 一套公开 API,可以用于软件和 Web 开发

其他

  • coder2gwy - 程序员考公指南
  • 955.WLB - 955 不加班的公司名单
  • programmer-job-blacklist - 程序员找工作黑名单,换工作和当技术合伙人需谨慎

高仿项目
命令行工具

官方安装包下载

32位 http://download.navicat.com.cn/download/navicat150_premium_cs_x86.exe
64位 http://download.navicat.com.cn/download/navicat150_premium_cs_x64.exe

注册补丁

百度网盘:https://pan.baidu.com/s/1RR17QvWBlekj2vDLEJzZdg 提取码:wqdy
阿里云盘:
TG:

官方安装包下载

32位 https://download.navicat.com.cn/download/navicat160_premium_cs_x86.exe
64位 https://download.navicat.com.cn/download/navicat160_premium_cs_x64.exe

无限试用方法

原理:清除注册表相关信息实现再次试用14天!试用 Navicat Premium 大部分版本

将以下内容保存成 reset.bat 并运行

@echo off

echo Delete HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPremium\Registration[version and language]
for /f %%i in ('"REG QUERY "HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPremium" /s | findstr /L Registration"') do (
reg delete %%i /va /f
)
echo.

echo Delete Info folder under HKEY_CURRENT_USER\Software\Classes\CLSID
for /f %%i in ('"REG QUERY "HKEY_CURRENT_USER\Software\Classes\CLSID" /s | findstr /E Info"') do (
reg delete %%i /va /f
)
echo.

echo Finish

pause

参考链接:

https://github.com/malaohu/reset-navicat-premium
https://github.com/Abeautifulsnow/navicat-premium-crack

下载

百度网盘:https://pan.baidu.com/s/1MtlteEcj9AeRrIfgs_hOag 提取码:wqdy
阿里云盘:
TG:

Set 数据类型

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用

Set 对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set 中的元素只会出现一次,即 Set 中的元素是唯一的

Set 中判断两个值是否相等的算法不同于 ===,在 Set 内部,两个 NaN 是相等

let a = [NaN, NaN, undefined, undefined, +0, -0, {}, {}]
let b = new Set(a) // [NaN, undefined, 0, {}, {}]

属性

Set.length 为 0
Set.prototype.size 返回 Set 对象值得个数

操作方法

Set.prototype.add(value) // 在尾部添加某个值,返回该 Set 本身
Set.prototype.delete(value) // 移除Set的中与这个值相等的元素,即如果该元素存在,返回true,否则返回false
Set.prototype.has(value) // 返回一个布尔值,表示该值是否为 Set 的成员
Set.prototype.clear() // 移除 Set 对象内的所有元素
Set.prototype.values() // 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值
Set.prototype.keys() // 与values()方法相同
Set.prototype.entries() // 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和Map对象保持相似,每个值的键和值相等
Set.prototype.forEach(callbackFn[, thisArg])
// Set 结构的键名就是键值(两者是同一个值),因此 forEach 遍历时 callbackFn 前两个参数是同一个值

Set Array 互相转化

Array.from(mySet) // Set ==> Array
[...mySet] // Set ==> Array
new Set(myArray) // Array ==> Set
[...new Set(myArray)] // 数组去重

使用 Set 实现并集(Union)、交集(Intersect)和差集(Difference)

let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])

// 并集
function union(setA, setB) {
return new Set([...setA, ...setB])
}

// 交集
function intersect(setA, setB) {
return new Set([...setA].filter(x => setB.has(x)))
}

// 差集
function difference(setA, setB) {
return new Set([...setA].filter(x => !setB.has(x)))
}

Map

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值
Map 中 NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等

属性

Map.length // 0
Map.prototype.size // 返回 map 对象键值对的个数

方法

Map.prototype.clear() // 移除 Map 对象的所有键/值对
Map.prototype.delete(key) // 如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false
Map.prototype.entries() // 返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组
Map.prototype.forEach(callbackFn[, thisArg]) // 按插入顺序,为 Map 对象里的每一键值对调用一次 callbackFn 函数。如果为 forEach 提供了 thisArg,它将在每次回调中作为 this 值
Map.prototype.get(key) // 返回键对应的值,如果不存在,则返回 undefined
Map.prototype.has(key) // 返回一个布尔值,表示 Map 实例是否包含键对应的值
Map.prototype.keys() // 返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键
Map.prototype.set(key, value) // 设置 Map 对象中键的值。返回该 Map 对象
Map.prototype.values() // 返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值

迭代 Map

for (var [key, value] of myMap) {
console.log(key + ' = ' + value)
}

myMap.forEach(function(value, key) {
console.log(key + ' = ' + value)
})

Map 和数组的关系

var kvArray = [['key1', 'value1'], ['key2', 'value2']]

// 使用常规的Map构造函数可以将一个二维键值对数组转换成一个 Map 对象
var myMap = new Map(kvArray)

myMap.get('key1') // 返回值为 'value1

// 使用 Array.from 函数可以将一个Map对象转换成一个二维键值对数组
console.log(Array.from(myMap)) // 输出和 kvArray 相同的数组

合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的

// 展开运算符本质上是将Map对象转换成数组
var merged = new Map([...map1, ...map2])

WeakMap

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/WeakMap

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的

正由于这样的弱引用,WeakMap 的 key 是不可枚举的,即无法遍历
WeakMap 没有size属性
没有clear()方法

symbol

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol

浏览器的组成

  • 用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
  • 浏览器引擎- 用来查询及操作渲染引擎的接口
  • 渲染引擎(浏览器内核)- 用来显示请求的内容,例如,如果请求内容为 html,它负责解析 html 及 css,并将解析后的结果显示出来
  • 网络- 用来完成网络调用,例如 http 请求,它具有平台无关的接口,可以在不同平台上工作
  • UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
  • JS 解释器- 用来解释执行 JS 代码
  • 数据存储- 属于持久层,浏览器需要在硬盘中保存类似 cookie 的各种数据,HTML5 定义了 Storage 技术,这是一种轻量级完整的客户端存储技术

主流的渲染引擎

浏览器的渲染引擎也叫排版引擎,或者是浏览器内核

主流的 渲染引擎 有

  • Chrome 浏览器: Blink 引擎(WebKit 的一个分支)。
  • Safari 浏览器: WebKit 引擎,windows 版本 2008 年 3 月 18 日推出正式版,但苹果已于 2012 年 7 月 25 日停止开发 Windows 版的 Safari。
  • FireFox 浏览器: Gecko 引擎。
  • Opera 浏览器: Blink 引擎(早期版使用 Presto 引擎)。
  • Internet Explorer 浏览器: Trident 引擎。
  • Microsoft Edge 浏览器: EdgeHTML 引擎(Trident 的一个分支)。

渲染引擎工作原理

渲染引擎解析的基本流程:

  • 解析 HTML 构建 DOM树,同时解析所有的 css 样式,构建 css 规则
  • 根据 DOM 树和 css 规则合并构建 渲染树
    • DOM 树上的节点没有样式的,渲染树的节点有样式的
    • 渲染树上的节点都是需要渲染的,所以渲染树上没有像head标签 或 display: none这样的元素,但是它们在 DOM 树中
  • 对渲染树进行布局,定位坐标和大小、确定是否换行、确定 position、overflow、z-index 等等,这个过程叫layoutreflow
  • 绘制渲染树,调用操作系统底层 API(UI Backend)进行绘图操作

webkit 内核工作流程

gecko 内核工作流程

重绘与回流

重绘与回流

回流(reflow): 又叫重排,当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。

重绘(repaint):当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color。

  1. 每个页面至少需要一次回流+重绘
  2. 回流必将引起重绘

回流什么时候发生?

  • 添加或者删除可见的 DOM 元素
  • 元素位置改变
  • 元素尺寸改变——边距、填充、边框、宽度和高度
  • 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变
  • 页面渲染初始化
  • 浏览器窗口尺寸改变(resize事件)发生时
var s = document.body.style
s.padding = '2px' // 回流+重绘
s.border = '1px solid red' // 再一次 回流+重绘
s.color = 'blue' // 再一次重绘
s.backgroundColor = '#ccc' // 再一次 重绘
s.fontSize = '14px' // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'))

聪明的浏览器

从上个实例代码中可以看到几行简单的 JS 代码就引起了 6 次左右的回流、重绘。而且我们也知道回流的花销也不小,如果每句 JS 操作都去回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护 1 个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前 flush 队列,这样浏览器的优化可能就起不到作用了。当你向浏览器请求一些 style 信息的时候,就会让浏览器 flush 队列,比如:

  • offsetTop, offsetLeft, offsetWidth, offsetHeight
  • scrollTop/Left/Width/Height
  • clientTop/Left/Width/Height
  • width,height
  • 请求了 getComputedStyle(), 或者 IE 的 currentStyle

如何性能优化

减少回流与重绘的次数,就需要简单对渲染树的操作

  • 直接使用 className 修改样式,少用 style 设置样式
  • 让要操作的元素进行”离线处理”,处理完后一起更新
    • 使用 DocumentFragment 进行缓存操作,引发一次回流和重绘
    • 使用 display:none 技术,只引发两次回流和重绘
  • 将需要多次重排的元素,position 属性设为 absolute 或 fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素为动画的 HTML 元素,例如动画,那么修改他们的 CSS 是会大大减小 reflow

diff

模板编译
模板渲染过程
路由

路由拦截

主要是利用 vue-router 提供的钩子函数 beforeEach() 对路由进行判断 - 导航守卫 - 全局前置守卫

router.beforeEach((to, from, next) => {
// ...
})
  • to: Route: 即将要进入的目标

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

  • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

  • next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

  • next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

vue 路由传参

_vue/vue?id=路由参数

  • vue 路由传参方式,说明区别和应用场景

编程式导航

params 和 query

params 要用 name 来引入,query 要用 path 来引入

query 类似于 ajax 中 get 传参,params 类似于 post

const userId = '123'
// 字符串
router.push('/user') // -> /user

// 对象, path为路由的path属性值
router.push({ path: '/user' }) // -> /user
router.push({ path: `/user/${userId}` }) // -> /user/123

// 命名的路由,name为路由的name属性值
router.push({ name: 'user', params: { userId }}) // -> /user/123
// 如果提供了 path,params 会被忽略
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

// 带查询参数
router.push({ path: 'register', query: { plan: 'private' }}) // -> /register?plan=private

routes: [{ path: '/user/:id?', name='user', component: User }]

首屏加载优化和算法

vue-loader

解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理

Computed和Watch

Computed 有缓存: 基于它们的响应式依赖进行缓存,只有跟计算属性相关的数据发生了改变,计算属性才会重新计算
Watch没有缓存性 deep: true 深度监听,这样便会对对象中的每一项进行监听。优化的话可以使用字符串形式监听

vue3 新特性

基于 Proxy 的观察者机制
重写虚拟 Dom
更好的支持 ts
新工具 vite

做过哪些 webpack 配置

配置 proxy 跨域
配置 externals
配置别名
添加打包分析插件 BundleAnalyzerPlugin

chainWebpack

const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV);

// cdn
const cdn = {
css: [],
js: [
'https://cdn.bootcss.com/vue/2.5.17/vue.runtime.min.js',
'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
]
}

module.exports = {
chainWebpack: config => {
// 添加别名
config.resolve.alias
.set('@', resolve('src'))

// 移除 prefetch 插件
config.plugins.delete('prefetch')
// 移除 preload 插件
config.plugins.delete('preload')

// 生产环境配置
if (isProduction) {
// 生产环境注入cdn , 还需要修改index.html
config.plugin('html')
.tap(args => {
args[0].cdn = cdn
return args
})
}
},

configureWebpack: config => {
// 配置 externals
// 键:表示 导入包语法 from 后面跟着的名称
// 值:表示 script 引入JS文件时,在全局环境中的变量名称
config.externals = {
'vue': 'Vue',
'element-ui': 'ELEMENT',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios'
}

// UglifyJsPlugin
// 去掉 console.log drop_console: true,

// 开启gzip压缩
// compression-webpack-plugin

},

css: {
// css预设器配置项
loaderOptions: {
// pass options to sass-loader
sass: {
// 引入全局变量样式,@使我们设置的别名,执行src目录
data: ` @import "@/stylePath/theme.scss"; `
}
}
},

devServer: {
proxy: {
'/api': {
target: 'http://yd.msword.top', // 代理的目标服务器地址 用‘/api'代替target里面的地址
// https请求需要该设置
secure: false,
ws: true, // 代理websockets
changeOrigin: true, // 是否跨域,虚拟的站点需要更管origin
pathRewrite: {
'^/api': '',
}
}
}
}
}

index.html 修改

<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<% } %>

vue 自定义指令

全局

Vue.directive('color', {
// 只调用一次,指令第一次绑定到元素时调用,在这里可以进行一次初始化设置;
bind (el, binding) {
console.log(binding)
el.style.background = binding.value
},
// 被绑定元素插入父节点时调用;
inserted () {},
// 在 bind 之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值;
update () {}
})

组件内

directives:{
test:{
inserted (el, binding) {// 指令的定义
// el为绑定元素,可以对其进行dom操作
console.log(binding) //一个对象,包含很多属性属性
},
bind (el, binding, vnode) {
el.innerHTML = binding.value
}
}
},

vuex 的五个状态

state

mutations:提供修改数据的方法

getters:可以认为是 store 的计算属性

actions:Action 提交的是 mutation,可以包含异步

modules:将 store 分割成模块

vuex 异步

const mutations = {
addTodo (state, playload) {
// 操作state
}
}
const actions = {
// 异步任务
// context : 相当于 store
// playload : 就是传过来的参数
addTodoAsync (context, playload) {
setTimeout(() => {
context.commit('addTodo', playload)
}, 1000)
}
}

methods: {
// 添加任务
addTodo () {
// 异步 : 分发 dispatch => actions
// dispatch => 找 actions
this.$store.dispatch('addTodoAsync', {
todoName: this.todoName
})
}
}

Hash 和 History 路由的区别和优缺点?

  • hash 路由模式的实现主要是基于下面几个特性:

URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送(hash 值改变,浏览器不会重新发起请求)
hash 值改变,会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制 hash 的切换
可以通过 a 标签,或对 loaction.hash 进行赋值,来改变 URL 的 hash 值
可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)

  • history 路由模式的实现主要基于存在下面几个特性:

pushState 和 repalceState 两个 API 来操作实现 URL 的变化,且不会重新发起请求
使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染)
history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

  • hash模式优缺点

优点

只需要前端配置路由表, 不需要后端的参与
兼容性好, 浏览器都能支持
hash值改变不会向后端发送请求, 完全属于前端路由

缺点
hash值前面需要加#, 不符合url规范, 也不美观

  • history 模式的优缺点:

优点:
符合url地址规范, 不需要#, 使用起来比较美观

缺点:
需要服务端配合重定向,否则一刷新页面就404了
兼容性比较差, 利用了 HTML5 History对象中新增的 pushState() 和 replaceState() 方法, 需要浏览器的支持

https://cn.vuejs.org/v2/guide/components-custom-events.html#将原生事件绑定到组件

文档类型<!DOCTYPE>

<!DOCTYPE html> 声明告诉浏览器按照HTML5规范解析页面

字符集

<meta charset="UTF-8" />

告诉浏览器当前 html 文档使用 UTF-8 进行的字符编码

link 标签 ref 属性值 prefetch preload 用于预加载

<link ref="preload" href="*.js" as="script" />
<!-- preload需要写上正确的as属性,如果不写或错误,等同于XHR请求,优先级非常低 -->
<link rel="preload" href="font.woff" as="font" crossorigin />
<!-- 预加载字体你还必须设置crossorigin 属性 -->
  • preload 是用于预加载当前页的资源,浏览器会优先加载它们(加载后并不执行,在需要执行的时候再执行)

将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件
提前加载指定资源,不再出现依赖的 font 字体隔了一段时间才刷出

  • prefetch 是用于预加载后续页面使用的资源,浏览器也会加载它们,但优先级不高

  • 避免混用 preload 和 prefetch,混用的话,并不会复用资源,而是会重复加载

<!-- 浏览器会加载两次改字体 -->
<link rel="preload" href="https://at.alicdn.com/t/font.woff" as="font">
<link rel="prefetch" href="https://at.alicdn.com/t/font.woff" as="font">

插入的脚本(无论在什么位置)在网络优先级中是很低级

script 标签的 defer 和 async 异步加载

这两个属性都告诉浏览器,它可以 “在后台” 加载脚本的同时继续解析 HTML,并在脚本加载完之后再执行。这样,脚本下载就不会阻塞 DOM 构建和页面渲染了

defer 和 async 之间的不同是他们开始执行脚本的时机的不同

async(H5) 一旦脚本可用,则会异步执行(仅适用于外部脚本) 脚本在它们完成下载完成后的第一时间执行,如果一个指定 async 的脚本很快就完成了下载,那么它的执行会阻塞 DOM 构建以及所有在之后才完成下载的同步脚本。

defer 规定当页面已完成解析后,执行脚本(仅适用于外部脚本) 脚本会按照它在 HTML 中出现的顺序执行,并且不会阻塞解析。

表格标签 table

table 属性 重点记住 cellspacing 、 cellpadding

  • cellspacing 设置单元格与单元格边框之间的空白距离
  • cellpadding 设置单元格内容与单元格边框之间的空白距离

table 合并单元格

  • 跨行合并(向下合并):rowspan=”合并单元格的个数”
  • 跨列合并(向右合并):colspan=”合并单元格的个数”

input file 控件

使用 input file 进行文件上传时,重复选择相同文件时,change 事件不再触发
解决方式:手动将 file 的 value 值设置为空

<input type="file" id="file" accept="image/gif,image/jpeg" @change="uploadFile($event)" />
$event.target.value = ''
// 或
var file = document.getElementById('file')
file.value = ''

HTML5 语义化标签

HTML5 新的语义化标签

header 头部、nav 导航、footer 底部、aside 侧边栏、article 文章、section 区块、main 主体区域

类名操作

js 在 H5 中给所有的 DOM 对象新增了一个属性 classList
classList 是一个集合,会存储某个元素上所有的类名,使用 classList 来替代 className 操作 class 类

// 添加类
div.classList.add('classname')
// 移除类
div.classList.remove('classname')
// 切换类
div.classList.toggle('classname')
// 判断类
div.classList.contains('classname')

自定义属性操作

H5 规定,以后但凡给标签增加自定义属性,都应该用 data- 开头
H5 给所有的 DOM 对象增加了一个 dataset 的属性,这个属性中会包含所有 data- 开头的属性

<div id="box" data-name="zs" data-age="10" data-user-name="ls"></div>
<script>
var box = document.querySelector('#box')
console.log(box.dataset) // DOMStringMap {name: 'zs', age: '10', userName: 'ls'}
box.dataset.aaBb = 'cc' // 在html结构中或添加 data-aa-bb="cc" 的自定义属性
</script>

注意:html 中属性是忽略大小写的,如果需要,应使用中划线 - 进行分隔,在 js 中会转换成驼峰的形式,如data-user-name ==> userName

网络状态

navigator.onLine 属性,是一个布尔值:脱机状态返回 false,在线状态返回 true

注意:返回 true 不代表不一定能访问互联网,因为有可能连接的是局域网。但是返回 false 则表示一定连不上网。

监听网络变化

// 网络连接时会被调用
window.addEventListener('online', function() {
alert('online')
})

// 当网络断开时会被调用
window.addEventListener('offline', function() {
alert('offline')
})

web 存储

特点:

  • 大小 4k
  • 生命周期:默认会话级别,但是可以设置过期时间
  • 数据可以在同一个网站的页面共享
  • 在请求时会自动携带
  • 以字符串形式存储,这个字符串有固定的格式:key=value;key1=value1;
  • 一般用于存储 sessionId,可以实现登录状态保持 (会话保持)
document.cookie = 'name=zhangsan'
document.cookie = 'age=18'
document.cookie = 'sex=23'

// 设置过期时间
document.cookie = 'sex=12;max-age=3600'

// 读取cookie
var result = document.cookie
console.log(result)

WebStorage

  • sessionStorage 和 localStorage 特点

    • 都保存在客户端
    • 大小为 5M 左右
    • 使用方法相同
    • 以键值对的方式,存储字符串格式的数据
  • sessionStorage 和 localStorage 区别

    • sessionStorage 生命周期默认为一个会话周期,且不能设置周期,一旦关闭浏览器,就销毁了,不能在不同页面共享数据
    • localStorage 永久生效,除非手动删除,可以多个窗口共享
  • 使用方法

setItem(key, value) // 设置存储内容
getItem(key) // 读取存储内容
removeItem(key) // 删除键值为key的存储内容
clear() // 清空所有存储内容

cookie

  • 大小受限
  • 用户可以操作(禁用)cookie,使功能受限
  • 安全性较低
  • 有些状态不可能保存在客户端
  • 每次访问都要传送 cookie 给服务器,浪费带宽

WebStorage

  • 存储空间更大:cookie 为 4KB,而 WebStorage 是 5MB
  • 对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage 会非常方便
  • WebStorage 不会随着 HTTP header 发送到服务器端,所以安全性相对于 cookie 来说比较高一些,不会担心截获,但是仍然存在伪造问题
  • WebStorage 数据操作比 cookie 方便

sessionStorage 多标签页共享

window.addEventListener('storage', function (event) {
if (event.key === 'token') {
this.sessionStorage.setItem('token', event.newValue)
}
})

文件读取

通过 FileReader 对象我们可以读取本地存储的文件(用户通过 input:file 上传的文件),可以使用 File 对象来指定所要读取的文件或数据。其中 File 对象可以是来自用户在一个<input>元素上选择文件后返回的 FileList 对象,也可以来自由拖放操作生成的 DataTransfer

files

对于 file 类型的 input 框,DOM 对象中存在一个 files 属性,这个属性是 FileList 对象,是一个伪数组,里面存储着上传的所有文件,当 input 框指定了 multiple 属性之后,就可以上传多个文件了。

file 对象

File 对象中包含了文件的最后修改时间、文件名、文件类型等信息。

FileReader 对象

FileReader 是一个 HTML5 新增的对象,用于读取文件(必须通过 input:file 上传)

var file = input.files[0]
// 创建一个fileReader对象
var fr = new FileReader
// 读取文件的两个方法
fr.readAsText(file) // 以文本的方式读取文件 ,文本文件
fr.readAsDataURL(file) // 以DataURL形式读取文件,图片,视频
// 文件读取完成事件:
fr.onload = function(){
// 当文件读取完成,可以通过result属性获取结果
console.log(fr.result)
}

图片预览

// 1. FileReader 是异步的
var file = document.getElementById('file')
var box = document.getElementById('box')
file.addEventListener('change', function() {
console.dir(this) // file 中files 属性里面存储了所有上传的文件
// 这个data就是我们上传的那个文件
var data = file.files[0]
// 1. 创建一个文件读取器
var fr = new FileReader()
// 2. 让文件读取器读取整个文件
fr.readAsDataURL(data)
// 3. 等待文件读取完
// onload:文件读取完成后,就会触发
fr.onload = function() {
// 通过 fr.result 就可以获取到最终的结果
var img = document.createElement('img')
img.src = fr.result
box.innerHTML = ''
box.appendChild(img)
}
})
// 2. URL.createObjectURL(file)  缺点:同步(阻塞),占用内存
var file = document.getElementById('file')
file.addEventListener('change', function() {
var data = this.files[0]
var result = URL.createObjectURL(data)
img.src = result
})

  • 新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf 为-1 或 includes 为false)就加入该新数组中
function unique(arr) {
var newArr = []
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return newArr
}
  • 数组下标判断:如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组
function unique(arr) {
var newArr = []
for (var i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) === i) {
newArr.push(arr[i])
}
}
return newArr
}
function unique(arr) {
return arr.filter((v, i, array) => array.indexOf(v) === i)
}
  • hash去重

虽然对象属性同样可以用来做数组去重,但是会将 number,NaN,undefined,null,变为字符串形式,因为对象的属性名就是一个字符串

function Deduplication(arr) {
var result = []
var hashMap = {}
for (var i = 0; i < arr.length; i++) {
var temp = arr[i]
if (!hashMap[temp]) {
hashMap[temp] = true
result.push(temp)
}
}
return result
}
Deduplication([[undefined, 'undefined']]) // [undefined]
  • ES6 Set
function unique(arr) {
var x = new Set(arr)
return [...x]
}
  • 对象数组去重
// arr: 目标数组
// id: 唯一属性
function distinct(arr, id) {
let map = new Map()
return arr.filter((item, index) => {
if (map.get(item[id])) return false
map.set(item[id], true)
return true
})
}

function uniqBy(arr, key) {
return [...new Map(arr.map((item) => [item[key], item])).values()]
}

数组去重

冒泡排序

详情
// 将数组中的数从小到大排列
var arr = [1, 4, 6, 7, 9, 3, 5, 8]
// var numi = 0
// var numj = 0
for (var j = 0; j < arr.length - 1; j++) {
// numj += 1
var flag = true
for (var i = 0; i < arr.length - 1 - j; i++) {
// document.write('(' + arr[i] + ',' + arr[i + 1] + ')')
// numi += 1
// 两两比较,如果前面的大于后面的,交换位置
if (arr[i] > arr[i + 1]) {
flag = false
var temp
temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
// document.write('交换了')
}
}
// document.write(',arr=(' + arr + ')')
// document.write('<br>')
// 如果一趟下来,一次交换都没有做,说明就已经排好序,就不需要继续比
if (flag) {
break
}
}

实现 call apply bind 方法

详情

call 和 apply 区别

call:

Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
var args = Array.prototype.slice.apply(arguments, [1]) // 获取额外参数
// var args = [...arguments].slice(1)
context.fn = this
var res = context.fn(...args)
delete context.fn
return res
}

context 为要绑定的 this,不传默认为 window
给 context 创建一个 fn 属性,并将值设置为需要调用的函数
调用 context.fn,并将额外参数 args 传递进去
删除 context 上的 fn 函数

apply:

Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let res
if (!arguments[1]) {
res = context.fn()
} else if (arguments[1].constructor.name === 'Array') {
res = context.fn(...arguments[1])
} else {
return console.error('Uncaught TypeError: CreateListFromArrayLike called on non-object')
// throw 'Uncaught TypeError: CreateListFromArrayLike called on non-object'
}
delete context.fn
return res
}

bind:

Function.prototype.myBind = function() {
var self = this // 保存原函数
var args = Array.prototype.slice.call(arguments) // 参数转为数组
// var args = [...arguments].slice(1) // 参数转为数组
var context = args.shift() // 保存需要绑定的this上下文
return function() {
// 返回一个新函数
self.apply(context, args.concat([].slice.call(arguments)))
}
}
function aaa(val, val1) {
console.log(val)
console.log(val1)
console.log(this.name)
}
aaa()
aaa.myCall({ name: '123' }, '121', 122)
aaa.myApply({ name: '123' }, ['121', 122])
aaa.myBind({ name: '123' })('111', '222')

防抖和节流

深浅拷贝

手写 Promise

手写 Promise.all

手写 Promise.race

异步调度

实现继承的方式

参考:https://www.cnblogs.com/humin/p/4556820.html

原型链继承,将父类的实例作为子类的原型,无法实现多继承

function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

构造继承,使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}

组合继承,通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

调用了两次父类构造函数,生成了两份实例

寄生组合继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

es6 中的继承

class Super {}
class Sub extends Super {}

const sub = new Sub()

Sub.__proto__ === Super

子类可以直接通过 __proto__ 寻址到父类

function Super() {}
function Sub() {}

Sub.prototype = new Super()
Sub.prototype.constructor = Sub

var sub = new Sub()

Sub.__proto__ === Function.prototype

而通过 ES5 的方式,Sub.__proto__ === Function.prototype

es5继承 与 es6 继承的区别参考 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/20

发布订阅

扁平数据转tree

实现 instanceof

// Left-hand instanceof Right-hand Right-hand.prototype 是否在 Left-hand 的原型链中
function myInstanceof(left, right) {
if (typeof left === 'undefined' || left === null) return false

// getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left)
while (true) {
// 查找到尽头,还没找到
if (proto == null) return false
// 找到相同的原型对象
if (proto == right.prototype) return true
proto = Object.getPrototypeof(proto)
}
}

实现一个 new 操作符

实现 JSON.stringify

手机号 3-4-4 分割
千分位格式化数字

function Student(name) {
this.name = name
}

let stu = new Student('zs') // Student {name: 'zs'}

new 做了哪些事

  1. 创建一个新的空对象,类型是 Student
  2. 将 this 指向这个新的对象
  3. 执行构造函数 目的:给这个新对象加属性和方法
  4. 返回这个新对象

简单实现

function NEW(fun) {
// 判断是否是函数
if (typeof fun !== 'function') {
throw new Error('第一个参数应该是函数')
}

// 创建一个空对象,并将原型指向 fun.prototype
// const newObj = {}
// newObj.__proto__ = fun.prototype
const newObj = Object.create(fun.prototype)

const argsArr = [].slice.call(arguments, 1)

// 将构造函数 this 指向 newObj,并执行构造函数
const result = fun.apply(newObj, argsArr)

// 如果构造函数本身有返回值,且返回值为对象时,会将本身返回值返回,如果返回值为简单类型,会忽略
const isObject = typeof result === 'object' && result !== null
const isFunction = typeof result === 'function'
if (isObject || isFunction) {
return result
}
// 返回新对象
return newObj
}
const stu = NEW(Student, 'ls')
console.log(stu) // Student {name: 'ls'}