diff --git a/.gitignore b/.gitignore deleted file mode 100644 index aa441854e..000000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -Thumbs.db -db.json -*.log -node_modules/ -public/ -.deploy*/ -package-lock.json diff --git "a/2020/06/07/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242/index.html" "b/2020/06/07/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242/index.html" new file mode 100644 index 000000000..3d3e54c4d --- /dev/null +++ "b/2020/06/07/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242/index.html" @@ -0,0 +1,234 @@ +从0开始搭建Hexo个人博客 | 淳淳同学的个人博客 + + + + + + + + + + + + +

从0开始搭建Hexo个人博客

+

搭建个人博客是每个程序员成长的必经之路,不但可以记录与分享自己在学习过程中Get到的新技能、新知识,还能顺便提高一下自己的文采。

+
+

Hexo简介

Hexo是一款基于Node.js的静态博客框架,可方便快捷的托管于GitHub上,是搭建博客的首选框架。

+

根据Hexo官网介绍,主要有以下四大优点:

+
    +
  • 超快速度: Node.js 所带来的超快生成速度,让上百个页面在几秒内瞬间完成渲染。
  • +
  • 支持 Markdown:Hexo 支持 GitHub Flavored Markdown 的所有功能,甚至可以整合 Octopress 的大多数插件。
  • +
  • 一键部署:只需一条指令即可部署到 GitHub Pages, Heroku 或其他平台。
  • +
  • 插件和可扩展性:强大的 API 带来无限的可能,与数种模板引擎(EJS,Pug,Nunjucks)和工具(Babel,PostCSS,Less/Sass)轻易集成
  • +
+

Hexo搭建步骤

安装Git

Git是目前世界上最先进的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。无论是个人代码管理还是团队合作开发中,学会git那都是百利而无一害的。如果对git还不是很了解,推荐去廖雪峰老师的博客或者先看一下Git Book的前三章。

+
1
2
3
4
5
# 安装命令
$ sudo apt-get install git

# 查看版本
$ git --version
+ +

安装Node.js

Hexo是基于Node.js环境运行的,所以需要安装Node环境及npm包管理工具。

+
1
2
3
4
5
6
7
8
9
10
11
# node安装命令
$ sudo apt-get install nodejs

# 查看node版本
$ node -v

# npm安装命令
$ sudo apt-get install npm

# 查看npm版本
$ npm -v
+ +

安装Hexo

1
2
3
4
5
6
7
8
9
10
11
# 利用npm全局安装hexo脚手架
$ npm install -g hexo-cli

# 查看hexo版本
$ hexo -v

# 删除hexo
$ npm uninstall -g hexo-cli

# 查看npm全局版本
$ npm ls -g --depth=0
+ +

创建博客项目

到此为止,装好了node环境以及hexo框架,基本上前期的环境配置就完成了,接下来就可以创建自己的博客项目了。

+
1
2
3
4
5
6
7
8
# 新建一个文件夹,如名为blog
$ mkdir blog

# 进入blog文件夹
$ cd blog

# 初始化hexo
$ hexo init
+ +

初始化成功后,blog文件夹下会出现如下文件:

+
    +
  • _config.yml: 博客的核心配置文件(设置主体、标题等属性)
  • +
  • package.json:项目所需的依赖包
  • +
  • source:用来存放你的文章
  • +
  • themes:放下下载的主题
  • +
  • public:存放生成的页面
  • +
  • scaffolds:生成文章的一些模板
  • +
+
1
2
# 安装所需依赖
$ npm install
+ +

安装成功后,会出现node_modules文件夹,文件夹内即安装的package.json内所有依赖包。接下来就可以配置并启动hexo了

+
1
2
3
4
5
6
7
8
9
10
11
# 清除缓存文件 (db.json) 和已生成的静态文件 (public)
$ hexo clean

# 生成静态文件,generate
$ hexo g

# 部署博客网站,deploy
$ hexo d

# 启动服务器,server
$ hexo s -g
+ +

运行成功后,浏览器打开http://localhost:4000便可看到你的hexo博客项目了,除了主题有点儿吃藕,还是挺不错的~

+

将Hexo部署到GitHub

这一步,我们就可以将hexo和GitHub关联起来,也就是将hexo生成的文章部署到GitHub上,打开站点配置文件_config.yml,翻到最后,修改为下面这样,其中LeeDebug改为你的GitHub账户名

+
1
2
3
4
deploy:
type: git
repo: git@github.com:LeeDebug/LeeDebug.github.io.git
branch: master
+ +

这个时候需要先安装deploy-git,也就是部署的命令,这样你才能用命令部署到GitHub。

+
1
$ npm install hexo-deployer-git --save
+ +

部署项目

+
1
2
3
$ hexo clean
$ hexo g
$ hexo d
+ +

部署成功后,浏览器打开http://LeeDebug.github.io,就能看到你的博客了

+

安装主题

首先下载主题包,如butterfly

+
1
npm i hexo-theme-butterfly
+ +

配置_config.yml文件

+
1
theme: butterfly
+ +

新建文章

首先修改/scaffolds/post.md文件模板,改为想要的形式,比如

+
1
2
3
4
5
6
7
8
9
10
11
---
title: {{ title }}
tags:
- Hexo
categorier:
- Hexo
keywords: "Hexo,笔记"
date: {{ date }}
description: "描述"
cover: https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg.png
---
+ +

利用post模板新建文章

+
1
hexo new post 文章标题
+ +

随后在source/_posts/文件夹下会出现文章标题的文件夹和文章标题.md的MarkDown文件,文章内容在*.md文件内编辑即可

+

新增分类页

1
hexo new page categories
+ +

/source/categories/index.md/这个文件改为以下内容

+
1
2
3
4
5
---
title: 分类
date: 2018-01-05 00:00:00
type: "categories"
---
+ + +

新增标签页

1
hexo new page tags
+ +

/source/tags/index.md/这个文件改为以下内容

+
1
2
3
4
5
---
title: 标签
date: 2018-01-05 00:00:00
type: "tags"
---
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/06/07/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E6%90%AD%E5%BB%BAHexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git a/2020/06/10/js-md5/index.html b/2020/06/10/js-md5/index.html new file mode 100644 index 000000000..8e7ba4023 --- /dev/null +++ b/2020/06/10/js-md5/index.html @@ -0,0 +1,174 @@ +js-md5 | 淳淳同学的个人博客 + + + + + + + + + + + + +

js-md5

+

前端在用户登录时尝使用md5对用户的登录信息进行加密操作

+
+

MD5简介

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

+

js-md5简介

A simple MD5 hash function for JavaScript supports UTF-8 encoding.
详情请查看npm的js-md5部分

+

使用教程

安装

1
$ npm i js-md5
+ +

引入

1
2
3
4
$ import md5 from 'js-md5'

// 如果在main.js中引入,需要注册
$ Vue.prototype.$md5 = md5
+ +

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
md5(''); // d41d8cd98f00b204e9800998ecf8427e
md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0

// It also supports UTF-8 encoding
md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07

// It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
md5([]); // d41d8cd98f00b204e9800998ecf8427e
md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e

// Different output
md5(''); // d41d8cd98f00b204e9800998ecf8427e
md5.hex(''); // d41d8cd98f00b204e9800998ecf8427e
md5.array(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126]
md5.digest(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126]
md5.arrayBuffer(''); // ArrayBuffer
md5.buffer(''); // ArrayBuffer, deprecated, This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
md5.base64(''); // 1B2M2Y8AsgTpgAmY7PhCfg==
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/06/10/js-md5/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/06/15/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243/index.html" "b/2020/06/15/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243/index.html" new file mode 100644 index 000000000..88d0e7ff7 --- /dev/null +++ "b/2020/06/15/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243/index.html" @@ -0,0 +1,237 @@ +程序员进修文档 | 淳淳同学的个人博客 + + + + + + + + + + + + +

程序员进修文档

+

以下文档为学习期间整理的好文,有的只是大体的看了一下觉得不错就收藏了,一定要找机会读完。以后要强烈拒绝这种码了不看的行为!!!

+
+

程序员修炼手册

+

前端面试题

前端100问:能搞懂80%的请把简历给我

JavaScript专题系列二十篇正式完结

web前端面试100题带答案(知乎)

+

学习资料

+

JavaScript

+

Vue

+

算法

+

浏览器

+

代码规范

+

Java

+

工具篇

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/06/15/%E7%A8%8B%E5%BA%8F%E5%91%98%E8%BF%9B%E4%BF%AE%E6%96%87%E6%A1%A3/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/06/20/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203/index.html" "b/2020/06/20/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203/index.html" new file mode 100644 index 000000000..c8c93a430 --- /dev/null +++ "b/2020/06/20/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203/index.html" @@ -0,0 +1,217 @@ +前端个人学习笔记与项目规范 | 淳淳同学的个人博客 + + + + + + + + + + + + +

前端个人学习笔记与项目规范

1
2
3
4
5
6
7
8
9
10
11
12
# Lee 更新于 2019-05-30

以下是学习Vue的渐进路线,并非适合所有人但适合大多数人
如果是计算机专业的话,而且有其他语言基础的,1~2天就可以过一遍
如果是非计算机专业的话,可能要半个月左右,不会的一定要勤问(发扬不要脸的精神)
看完之后别逗留太久,尽早跟着项目走,真正开始动手写点儿东西的时候,才是你刚开始入门的时候



人生、工作的结果 = 思维方式 × 热情 × 能力

--- 稻盛和夫《活法》
+ + +

Basic Document:

+

Basis SoftWare:

baiduCloud链接(Mac版):链接:https://pan.baidu.com/s/1dQJAJ8nSV2FyOcZt19KP5Q 密码:x605

+

baiduCloud链接(Windows版):链接:https://pan.baidu.com/s/1Imq9YggdknZOegJc8LNr8A 密码:qo6z

+
    +
  • IDE:Websotrm
  • +
  • GIt仓库管理:Sourcetree(用baiduCloud里的sourcetree276a.zip)
  • +
  • 接口测试:Postman
  • +
  • 浏览器(推荐):chrome
  • +
  • 常用fq:Shadow–socks (敏感词,下载的时候把–去掉)
  • +
+

开会内容:新项目的统一规范(2019-02-22)

1. Form封装问题

1
能在一个页面中写完的内容就不要随便抽象出一个组件,意义不大。
+ +

2. 组件引用的路径:

1
2
3
为了方便代码复用,统一使用绝对路径(如:'@/components/LqPagination'),按住Commond键 + 左键单击 即可跳转到该组件
PS:如果你的webStorm绝对路径无法跳转,请根据以下操作引入webpack文件:
webStorm --> preferences --> Languages & Frameworks --> JavaScript --> Webpack:../qf_admin/admin/build/webpack.base.conf.js
+ +

3. 分页组件:

1
2
3
4
5
为了解决分页时的loading问题,给分页组件传参数
LqPagination组件:【:init-func="initData"】
Pagination组件:【@pagination="initData"】
每个函数只做一件事情,initData()就只是一个初始化列表函数,不要写别的内容。
![image](http://note.youdao.com/yws/res/7075/13C1D152E20F48649D28553B9D5B309A)
+ +

4. 混入

1
2
每个模块的混入文件有且仅有一个,放在该模块主目录下的mixin里即可。
混入文件内仅存放Vuex的相关代码,不要放其他的
+ +

5. Vuex的使用

1
2
3
4
数据缓存的问题,以动态控制keep-alive来解决
【能不用Vuex的地方就不要用】
码表必须要用Vuex维护,并且必传:config = { usingCache: true }
![image](http://note.youdao.com/yws/res/7086/3C16AC303C164E6A8B4D22C81C0332FC)
+ +

6. 命名规范 & 页面规范

项目目录为:

 - views // 项目文件目录 */
+    - target-action // 日程模块 */
+        - components // 此模块公用的组件 */
+            - scheduleTable.vue
+        - mixin // 此模块公用的混入文件 */
+            - Data.js
+        - index.vue // 模块主页 */
+        - personal // 子模块 */
+            - month.vue
+        - create.vue // 新增 || 修改 页面 */
+    - announcement // 公告栏模块 */
+    - ... // 其他模块 */
+    
+
+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 文件夹 & 文件 命名规范
全部为小写,多个单词的话中间以 _ 【下划线】连接,如:
target_action,day_schedule,customer_info

# vue文件内部name命名规范
***.vue页面的name(同其在路由中的name),按照此文件的目录结构【小驼峰】命名,如:
1. 日程模块主页的name命名为:targetActionIndex
2. 日程模块新增页面的name命名为:createTargetAction
3. target-action的子模块personal的month页面的name命名为:targetActionPersonalMonth

# 函数命名规范
按照函数的具体功能【小驼峰】命名,如:
1. 初始化数据函数:initData()
2. 刷新table函数:handleFilter()
3. 选中当前行函数:handleCurrentChange()
4. 提交 || 确认 函数:handleConfirm()

# 页面格式规范
整体section内:class="app-container"(即padding="20px";)
如xxc_admin项目所有页面都封装在el-card中,已自带padding:20px,所以就不需要class="app-container"了
常规左右分栏布局:元素多的一侧 :span=14,少的一侧 :span=10
页面最下方距离底部尽量留出50px~60px的距离,以免页面宽度缩小之后有的元素被换行而挡住
高度实在放不开的页面,设置滚动:height: 800px;overflow:auto;overflow-x:hidden;

# 按钮样式规范
【table内】的按钮:放到el-tooltip中,提示框在上面,按钮size="mini",区分type,非圆角,带icon
【查询参数后】的按钮:round,正常sizi,区分type,带icon
【增加/修改页面】的按钮:非圆角,正常大小
「左边是“提交”,type="success" icon="el-icon-check"」
「右边是“作废”,type="danger" <svg-icon icon-class="ic-ban" style="margin-right: 5px;"/>」
【详情页面】的按钮:“返回”放到最右边,type="warning" icon="el-icon-back" @click="gotoAndClose()"
1. 增加:type=success,icon=plus
2. 删除:type=danger,icon=delete
3. 修改:type=primary,icon=edit(如果有两种修改按钮时:icon=edit-outline)
4. 查询:type=default,icon=search
5. 刷新:type=default,icon=refresh
6. 确定:type=success,icon=success
7. 取消:type=warning,icon=error
8. 提交:type=success,icon=check
9. 关闭:type=warning,icon=close
10. 详情:type=info,icon=info
11. 打印:type=info,icon=printer
12. Excel:type=success,icon=document

# 全局table样式规范
1. 按照客户的需求,全局修改css
2. 如果右侧有操作列,需fixed="right"固定在最右侧,且设置固定宽度
3. 能设置固定宽度的尽量设定,如:时间、日期、姓名(不会超过5个字)
4. 序号列,可有可无,如果表格字段过少,可充数用

# 函数执行成功后的提示
1. 创建、修改、更新、删除【成功】后统一用this.$notify,即notification
2. 【操作错误、参数错误】等提醒用this.$message
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/06/20/%E5%89%8D%E7%AB%AF%E4%B8%AA%E4%BA%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B8%8E%E9%A1%B9%E7%9B%AE%E8%A7%84%E8%8C%83/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/06/23/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213/index.html" "b/2020/06/23/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213/index.html" new file mode 100644 index 000000000..cadac00bf --- /dev/null +++ "b/2020/06/23/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213/index.html" @@ -0,0 +1,210 @@ +《前端内参》读书笔记 | 淳淳同学的个人博客 + + + + + + + + + + + + +

《前端内参》读书笔记

+

做web前端开发也有两年的时间了,但技术层面一直很浅,特别是近期感觉遇到了知识瓶颈,还是因为看书少、不爱总结。本次笔记在参考Bob Ma大佬整理分享的《前端内参》的基础上,记录并整理下来一些自己平时不注意的知识点。

+
+

壹.1.1.3 ES 8 新特性

字符串追加

在 ES 8 中String新增了两个实例函数String.prototype.padStartString.prototype.padEnd,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。

+
    +
  • String.padStart(targetLength,[padString])
    targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
    padString:(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为空格。
  • +
  • String.padEnd(targetLength,padString]) 参数释义同上。
  • +
+
1
2
3
4
5
6
7
8
9
10
11
'es8'.padStart(2);          // 'es8'
'es8'.padStart(5); // ' es8'
'es8'.padStart(6, '1891'); // '189es8'
'es8'.padStart(14, 'coffe'); // 'coffecoffeces8'
'es8'.padStart(7, '0'); // '0000es8'

'es8'.padEnd(2); // 'es8'
'es8'.padEnd(5); // 'es8 '
'es8'.padEnd(6, '1891'); // 'es81891'
'es8'.padEnd(14, 'coffe'); // 'es8coffecoffec'
'es8'.padEnd(7, '9'); // 'es89999'
+ + +

异步函数

Async Functions也就是我们常说的Async/Await,相信大家对于这个概念都已经不陌生了。Async/Await是一种用于处理JS异步操作的语法糖,可以帮助我们摆脱回调地狱(callback hell),编写更加优雅的代码。

+

通俗的理解,async关键字的作用是告诉编译器对于标定的函数要区别对待。当编译器遇到标定的函数中的await关键字时,要暂时停止运行,等到await标定的函数处理完毕后,再进行相应操作。如果该函数fulfiled了,则返回值是fulfillment value,否则得到的就是reject value。

+

下面通过拿普通的promise写法来对比,就很好理解了:

+
1
2
3
4
5
6
7
8
9
10
11
12
async function asyncFunc() {
const result = await otherAsyncFunc();// otherAsyncFunc()返回一个Promise对象
console.log(result);
}

// 等同于:
function asyncFunc() {
return otherAsyncFunc()// otherAsyncFunc()返回一个Promise对象
.then(result => {
console.log(result);
});
}
+ +

按顺序处理多个异步函数的时候优势更为明显:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function asyncFunc() {
const result1 = await otherAsyncFunc1();// otherAsyncFunc1()返回一个Promise对象
console.log(result1);
const result2 = await otherAsyncFunc2();// otherAsyncFunc2()返回一个Promise对象
console.log(result2);
}

// 等同于:
function asyncFunc() {
return otherAsyncFunc1()// otherAsyncFunc1()返回一个Promise对象
.then(result1 => {
console.log(result1);
return otherAsyncFunc2();// otherAsyncFunc2()返回一个Promise对象
})
.then(result2 => {
console.log(result2);
});
}
+ +

并行处理多个异步函数:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function asyncFunc() {
const [result1, result2] = await Promise.all([
otherAsyncFunc1(),// otherAsyncFunc1()返回一个Promise对象
otherAsyncFunc2() // otherAsyncFunc2()返回一个Promise对象
]);
console.log(result1, result2);
}

// 等同于:
function asyncFunc() {
return Promise.all([
otherAsyncFunc1(),// otherAsyncFunc1()返回一个Promise对象
otherAsyncFunc2() // otherAsyncFunc2()返回一个Promise对象
])
.then([result1, result2] => {
console.log(result1, result2);
});
}
+ +

处理错误:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function asyncFunc() {
try {
await otherAsyncFunc();// otherAsyncFunc()返回一个Promise对象
} catch (err) {
console.error(err);
}
}

// 等同于:
function asyncFunc() {
return otherAsyncFunc()// otherAsyncFunc()返回一个Promise对象
.catch(err => {
console.error(err);
});
}
+ +

壹.1.1.4 ES 9 新特性

异步迭代器 Asynchronous Iteration

async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:

+
1
2
3
4
5
async function func(array) {
for (let i of array) {
await someFunc(i);
}
}
+ +

这段代码不会达到预期目的,下面这段同样也不会:

+
1
2
3
4
5
async function func(array) {
array.forEach(async i => {
await someFunc(i);
});
}
+ +

上面这段代码中,循环本身依旧保持同步,并在内部异步函数之前全部调用完成。
引入异步迭代器后,就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for…of循环一起使用,以串行的方式运行异步操作。

+
1
2
3
4
5
async function func(array) {
for await (let i of array) {//异步迭代
someFunc(i);
}
}
+ +

更多详细论述见“壹.2.12”。

+

重新修订了字面量的转义

ES9 之前,\u表示unicode转义,\x表示十六进制转义,\后跟一个数字表示八进制转义,这使得创建特定的字符串变得不可能,例如Windows文件路径C:\uuu\xxx\111

+

要取消转义序列的语法限制,可在模板字符串之前使用标记函数String.raw。

+
1
2
3
4
5
let s = `\u{54}` //会转义成unicode "T"
console.log(s);//>> T

let str = String.raw`\u{54}`; //不会被转义
console.log(str);//>> \u{54}
+ +

Rest / Spread 属性

这个就是我们通常所说的三个点...,在=左边的是rest参数,放在=右边或者作为参数的是扩展运算符,这项特性在ES6中已经引入,但是ES6中的作用对象仅限于数组。在ES9中,为对象提供了像数组一样的rest参数和扩展运算符:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {
a: 1,
b: 2,
c: 3
};
const { a, ...param } = obj; //这里...是rest参数
console.log(a); //>> 1
console.log(param); //>> {b: 2, c: 3}

function foo({ a, ...param }) {//这里...是扩展运算符
console.log(a); //>> 1
console.log(param); //>> {b: 2, c: 3}
}
+ +

正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许匹配回车换行。

+
1
2
3
4
/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true
console.log(/hello.world/s.test(`hello
world`)) //>> true
+ +

正则表达式命名捕获组

未看

+

正则表达式后行断言

未看

+

正则表达式 Unicode 转义

未看

+

壹.1.1.5 ES 10 新特性

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/06/23/%E3%80%8A%E5%89%8D%E7%AB%AF%E5%86%85%E5%8F%82%E3%80%8B/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/07/01/LeetCode\345\270\270\350\247\201\351\242\230/index.html" "b/2020/07/01/LeetCode\345\270\270\350\247\201\351\242\230/index.html" new file mode 100644 index 000000000..a4d47b8d4 --- /dev/null +++ "b/2020/07/01/LeetCode\345\270\270\350\247\201\351\242\230/index.html" @@ -0,0 +1,201 @@ +LeetCode常见题 | 淳淳同学的个人博客 + + + + + + + + + + + + +

LeetCode常见题

+

算法对前端的重要性可以说至关重要,不可小觑。

+
+

算法思想

    +
  • 基础技巧: 分治、二分、贪心
  • +
  • 排序算法: 快速排序、归并排序、计数排序
  • +
  • 搜索算法: 回溯、递归、深度优先遍历,广度优先遍历,二叉搜索树等
  • +
  • 图论: 最短路径、最小生成树
  • +
  • 动态规划: 背包问题、最长子序列
  • +
+

- 双指针

+
    +
  1. 合并两个有序数组
  2. +
+
+

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

+

说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

+

示例:

+
    +
  • 输入:
      +
    • nums1 = [1,2,3,0,0,0], m = 3
    • +
    • nums2 = [2,5,6], n = 3
    • +
    +
  • +
  • 输出:
      +
    • [1,2,2,3,5,6]
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @param {number[]} nums1
* @param {number} m
* @param {number[]} nums2
* @param {number} n
* @return {void} Do not return anything, modify nums1 in-place instead.
*/
var merge = function(nums1, m, nums2, n) {
var i = m - 1, j = n - 1, len = m + n -1
while (i >= 0 || j >= 0) {
if (i < 0) {
nums1[len--] = nums2[j--]
} else if (j < 0) {
nums1[len--] = nums1[i--]
} else if (nums1[i] > nums2[j]) {
nums1[len--] = nums1[i--]
} else {
nums1[len--] = nums2[j--]
}
}
};
+ +

- 排序

- 贪心思想

- 二分查找

- 分治

- 搜索

- 动态规划

- 数学

数据结构相关

    +
  • 数组与链表:单/双向链表
  • +
  • 栈与队列
  • +
  • 哈希表
  • +
  • 堆:最大堆/最小堆
  • +
  • 树与图:最近公共祖先、并查集
  • +
  • 字符串:前缀树(字典树) /后缀树
  • +
+

- 链表

- 树

- 栈和队列

- 哈希表

- 字符串

- 数组与矩阵

- 图

- 位运算

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/07/01/LeetCode%E5%B8%B8%E8%A7%81%E9%A2%98/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/07/06/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244/index.html" "b/2020/07/06/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244/index.html" new file mode 100644 index 000000000..86019ce60 --- /dev/null +++ "b/2020/07/06/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244/index.html" @@ -0,0 +1,228 @@ +Ubuntu 16.04 部署命令 | 淳淳同学的个人博客 + + + + + + + + + + + + +

Ubuntu 16.04 部署命令

+

现时代身为一名前端工程师,配置服务器、部署线上项目等技能是必不可少的。

+
+

参考文档

+

配置步骤

基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 连接
ssh -p 22 root@47.104.223.131

# 修改主机名
sudo vi /etc/hostname
改为:
LeeJs

# 修改配置文件
sudo vi /etc/hosts
添加:
127.0.1.1 LeeJs

# 重启
reboot

# apt 检测更新
sudo apt update
apt list --upgradable

# 查看版本信息
lsb_release -a
uname -a
cat /proc/version
+ +
+

** ⚠️ 注:以下命令已打包至 install.sh **

+

安装yum(不需要)

1
apt install yum
+ + +

安装 ruby(不需要)

1
apt install ruby
+ + +

安装 LinuxBrew(不需要)

1
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
+ + +

安装 screen

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
apt install screen

# 查看路径
which screen

# 查看版本
screen -v

# 查看所有
screen -ls

# 新建
screen -S qf_backend

# 恢复之前在线的screen,并置位离线
screen -x -d qf_backend/PID

# 重连
screen -r qf_backend/PID
ctrl + r ==> 输入PID

# 先重连,若找不到离线作业,则新建
screen -R qf_backend/PID

# 删除screen
screen -X -S PID quit
+ + +

安装 git

1
apt install git
+ + +

安装 pyenv

1
2
3
4
5
6
7
8
9
10
11
12
sudo curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

# 添加源
sudo vi ~/.bashrc

# 添加下面三行
export PATH="/root/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

# 更新源
source ~/.bashrc
+ + +

安装「 python 所需依赖包 」

1
apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline6 libreadline6-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev zlib*
+ + +
    +
  • ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
    1
    apt install libbz2-dev libssl-dev libsqlite3-dev libreadline6 libreadline6-dev
    + + +
  • +
+

通过 pyenv 安装 python

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
33
34
35
36
# 下载 python
wget http://mirrors.sohu.com/python/3.6.5/Python-3.6.5.tar.xz -P ~/.pyenv/cache/

# 查看目录
cd ~/.pyenv/cache/

# 所有支持版本
pyenv install --list

# 安装指定版本python,-v:显示全过程
pyenv install 3.6.5 -v

# 刷新pyenv
pyenv rehash

# 查看所有的版本
pyenv versions

# 安装pyenv-virtualenv插件
git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

# 以python 3.6.5 新建名为 qf_admin_3.6.5 的虚拟环境
pyenv virtualenv 3.6.5 qf_admin_3.6.5

# 激活虚拟环境
pyenv activate qf_admin_3.6.5

# 离开已经激活的环境
pyenv deactivate

# 设置当前路径的python版本,优先级:shell > local > global
pyenv local 3.6.5

# 删除虚拟环境(删除所在目录即可)
rm -rf ~/.pyenv/versions/test3.7.3/

+ + +

安装 mysql

    +
  • 如何在Ubuntu 16.04下安装MySQL
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 安装 mysql 基础服务
    apt install mysql-server
    apt install mysql-client
    apt install libmysqlclient-dev

    # 测试是否安装成功
    netstat -tap | grep mysql

    # 打开数据库
    mysql -uroot -p

    # 配置文件
    sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

    # 重启 mysql 服务
    service mysql restart
    + + +
  • +
+

安装 nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 安装 nginx
apt install nginx

# 查看nginx安装路径
whereis nginx

# 查看 nginx 加载的哪个配置文件
sudo nginx -t

# 配置文件位置
cat /etc/nginx/nginx.conf

# 启动
nginx

# 重启
nginx -s reload
+ + +

配置 HTTPS 443

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 服务器报错 400
You're accessing the development server over HTTPS, but it only supports HTTP.

# 安装 pyOpenSSL(测试时用)
pip install pyOpenSSL

# 以 https 方式启动 django(测试专用)
python manage.py runserver_plus --cert cert.crt 0.0.0.0:23480

--------------------------------

# SSL 证书
阿里云:https://yundunnext.console.aliyun.com/?spm=5176.2020520163.aliyun_sidebar.daliyun_sidebar_cas.5f3956a7FAnrMY&p=cas#/overview/cn-hangzhou
下载并解压后放在:/etc/nginx/certificate/

# nginx https 配置
server
{
listen 443;
server_name www.lee521.top;
ssl on;
root html;
index index.html index.htm;
ssl_certificate /etc/nginx/certificate/2501940_lee521.top.pem;
ssl_certificate_key /etc/nginx/certificate/2501940_lee521.top.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
location ~ /api {
proxy_pass http://127.0.0.1:23480;
}
location ~ /doc {
proxy_pass http://127.0.0.1:23480;
}
location ~ /static {
proxy_pass http://127.0.0.1:23480;
}
}

# 配置成功 !
https://www.lee521.top/doc/
https://www.lee521.top/api/system/system_user/
+ + +

配置前端

1
2
3
4
5
server {
listen 10086;
server_name localhost;
root /root/www/vue-element-admin/dist/;
}
+ + +

打包 install.sh 脚本文件

==执行 source ~/.bashrc 会出错,提示 source not found,可以分两批执行==

+
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
33
34
35
sudo apt update
apt list --upgradable
# need keyboard input: y
sudo apt install yum
# need keyboard input: y
sudo apt install ruby
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
# need keyboard input: y
sudo apt install screen
sudo apt install git
# install pyenv
rm -rf ~/.pyenv/
sudo curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

# update source
sed -i '1i export PATH="/root/.pyenv/bin:$PATH"' ~/.bashrc
sed -i '2i eval "$(pyenv init -)"' ~/.bashrc
sed -i '3i eval "$(pyenv virtualenv-init -)"' ~/.bashrc
# 执行这一句会出错,提示 source not found
source ~/.bashrc

# install depend package
sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev
# download python 3.6.5
wget http://mirrors.sohu.com/python/3.6.5/Python-3.6.5.tar.xz -P ~/.pyenv/cache/
# install 2.6.5 with pyenv
pyenv install 3.6.5 -v
# refresh pyenv
pyenv rehash
# clone pyenv-virtualenv
git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
# new a virtualenv named custom_3.6.5 with python3.6.5
pyenv virtualenv 3.6.5 custom_3.6.5
# show all virtualenv
pyenv versions
+ +

新版项目启动代码

1
gunicorn -c ./gunicorn-config.py backend.wsgi
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/07/06/Ubuntu-16-04-%E9%83%A8%E7%BD%B2%E5%91%BD%E4%BB%A4/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/07/18/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211/index.html" "b/2020/07/18/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211/index.html" new file mode 100644 index 000000000..9f67806ae --- /dev/null +++ "b/2020/07/18/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211/index.html" @@ -0,0 +1,411 @@ +你不知道的JavaScript(上卷) | 淳淳同学的个人博客 + + + + + + + + + + + + +

你不知道的JavaScript(上卷)

+

去年就想读这一套书,由于个人原因一直拖到现在,今天终于周末在家没事,看了5个小时才看了上卷的1/3(其实也就60页)。主要讲述了作用域和闭包的相关知识,可能是由于自己基础知识的匮乏,所以进度有些慢。总之,要提高阅读速度了。

+
+

作用域和闭包

作用域是什么

    +
  • JavaScript是一门编译语言,其引擎进行编译的步骤和传统的编译语言非常相似,包括下列三个编译步骤:
      +
    • 分析/词法分析(Tokenizing/Lexing)(词法化、单词化)
    • +
    • 解析/语法分析(Parsing)
    • +
    • 代码生成
    • +
    +
  • +
+
    +
  • 作用域是一种规则,用于确定在何处以及如何查找该变量(标识符),即用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找
      +
    • 引擎
    • +
    • 编译器
    • +
    • 作用域
    • +
    +
  • +
+
    +
  • var a = 2;
      +
    • var a:编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。(定义声明在编译阶段进行)
    • +
    • a = 2:编译器会为引擎生成运行时所需要的代码,这些代码被用来处理a=2这个赋值操作。首先会询问作用域,在当前作用域集合中是否存在一个叫作a的变量。如果是,引擎就会使用这个变量;否则会向外层嵌套的作用域继续查找该变量。(赋值声明会被留在原地等待执行阶段被调用)
    • +
    +
  • +
+
    +
  • 编译器在编译过程中,对变量有两种查询方式
      +
    • LHS查询:变量在左侧,目的为赋值操作
    • +
    • RHS查询:变量在非左侧,目的为获取变量的值
    • +
    +
  • +
+
    +
  • LHS和RHS都会再当前执行作用域中开始
      +
    • 不成功的RHS会导致抛出ReferenceError异常
    • +
    • 不成功的LSH会导致自动隐式的创建一个全局变量(非严格模式下),该变量使用LHS查询的目标作为标识符,或者抛出ReferenceError异常(严格模式下)
    • +
    +
  • +
+
    +
  • LHS和RHS的异常错误
      +
    • ReferenceError异常:同作用域判别失败相关
    • +
    • TypeError异常:作用域判别成功,但对结果的操作是非法或不合理的
    • +
    +
  • +
+
    +
  • 编译器可以再代码生成的同时处理声明和值的定义
  • +
+
    +
  • 在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量或抵达最外层作用域(即全局作用域)为止
  • +
+

词法作用域

    +
  • 作用域的两种工作模型
      +
    • 词法作用域:在写代码或定义时确定的,作用域链基于作用域的嵌套,即更关注在何处声明。大多数编程语言都采用
    • +
    • 动态作用域:在运行时确定的,作用域链是基于调用栈的,即更关注函数是从何处调用的。this的机制也是如此
    • +
    +
  • +
+
    +
  • 词法作用域:是一套关于引擎如何寻找以及会在何处找到变量的规则
  • +
+
    +
  • JavaScript中有两个机制可以“欺骗”词法作用域。但在编译时引擎均无法对作用域查找进行优化,所以不要使用它们
      +
    • eval(…):生成代码并运行
    • +
    • with:会产生内存泄漏
    • +
    +
  • +
+
    +
  • 箭头函数:ES6添加了一个特殊的语法形式用于函数声明,将this同词法作用域联系起来
  • +
+
    +
  • 箭头函数在涉及this绑定时的行为和普通函数的行为完全不一致。它放弃了所有普通this绑定的规则,取而代之的是用当前的词法作用域覆盖了this本来的值
  • +
+

函数作用域和块作用域

    +
  • 函数作用域:属于这个函数的全部变量都可以再整个函数的范围内使用及复用(事实上在嵌套的作用域内也可以使用)
  • +
+
    +
  • 基于作用域的隐藏方法:大豆是从最小特权原则(最小授权原则、最小暴露原则)中引申出来的,即应该最小限度的暴露必要内容
  • +
+
    +
  • 如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式
  • +
+
    +
  • 函数声明和函数表达式之间最重要的区别:他们的名称标识符将会绑定在何处
  • +
+
    +
  • 函数表达式的应用场景:
      +
    • 匿名函数表达式:setTimeout(() => {}, 0)
    • +
    • 立即执行函数表达式-IIFE(Immediately Invoked Function Expression):(function foo(){...})()(function(){...}())
    • +
    +
  • +
+
    +
  • 匿名函数表达式的缺点:(==养成始终给函数表达式命名的好习惯==)
      +
    • 在栈追踪中不会显示出有意义的函数名,使调试很困难
    • +
    • 没有函数名,自身引用自身,只能使用过期的arguments.callee来引用
    • +
    • 可读性差,可理解性差
    • +
    +
  • +
+
    +
  • 除了JavaScript歪的很多编程语言都支持块作用域(表面上看没有,除非更加深入的研究)P30页
  • +
+
    +
  • 块作用域的应用场景:
      +
    • with:从对象中创建出的作用域仅在with声明中而非外部作用域中有效
    • +
    • try/catch:ES3规范中规定catch分句会创建一个块作用域
    • +
    • let:为其声明的变量隐式的创建了一个块作用域
    • +
    • const:创建块作用域变量,但值是固定的常亮
    • +
    • ???
    • +
    +
  • +
+
    +
  • ==for循环的let i==:
      +
    • 将i重新绑定到循环的每一个迭代中,并确保使用上一个循环迭代结束时的值重新进行赋值
    • +
    • i在循环过程中不止被声明一次,每次迭代都会被声明,随后的每次迭代都会使用上一个迭代结束时的值来初始化下一个i
    • +
    +
  • +
+
    +
  • 为变量显式声明块作用域,并对变量进行本地绑定是非常有用的==工具==,P34页
  • +
+

提升

    +
  • 任何声明在某个作用域的变量,都将属于这个作用域
  • +
+
    +
  • 包括变量和函数在内的所有声明都会再任何代码被执行前首先被处理,可以将这个过程形象的想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端
  • +
+
    +
  • 先有声明,后有赋值
  • +
+
    +
  • 在提升中,函数声明会首先被提升,然后才是变量声明;但出现在后面的函数声明还是可以覆盖前面的
  • +
+

作用域闭包

    +
  • 闭包是基于词法作用域书写代码时所产生的自然结果
  • +
+
    +
  • 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行
  • +
+
    +
  • 无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包
  • +
+
    +
  • 闭包的应用场景:
      +
    • function调用setTimeout(...)
    • +
    • 定时器、事件监听器、Ajax请求、跨窗口通信、WebWorkers、任何异步或同步任务,只要使用了==回调函数==,实际上就是在使用闭包
    • +
    • for循环
    • +
    • 立即执行函数表达式IIFE:(function(){...})())(本身创建了闭包,但严格来说并不是闭包)
    • +
    • 模块模式:比如jQuery和$符就是jQuery模块的公共api
    • +
    +
  • +
+
    +
  • 延迟函数的回调会在循环结束时才执行,即使定时器是setTimeout(..., 0)
  • +
+
    +
  • 在迭代内使用IIFE会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问
  • +
+
    +
  • let声明,可以用来劫持块作用域,并且在这个块作用域中声明一个变量;本质上这是将一个块转换成一个可以被关闭的作用域
  • +
+
    +
  • 最常见的实现模块模式的方法被称为模块暴露
  • +
+
    +
  • 模块模式需要具备的两个必要条件:
      +
    • 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)
    • +
    • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有属性
    • +
    +
  • +
+
    +
  • 模块的两个主要特征:
      +
    • 为创建内部作用域而调用了一个包装函数
    • +
    • 包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包
    • +
    +
  • +
+
    +
  • 模块也是普通函数,也可以接受参数
  • +
+
    +
  • 模块模式另一个简单而强大的变化用法是,命名将要作为公共api返回的对象(大多数模块依赖加载器/管理器的本质都是将这种模块定义封装进一个友好的api)
  • +
+
    +
  • import:将一个模块中的一个或多个api导入到当前作用域,并可以分别绑定在不同的变量上
  • +
  • module:将整个模块的api导入并绑定 到一个变量上
  • +
  • export:将当前模块的一个标识符(变量或函数)导出为公共api
  • +
+

this和对象原型

+

任何足够先进的技术都和魔法无异。

+
+
+

在遇到问题时,许多开发者并不会深入思考为什么this的行为和预期的不一致,也不会试图回答那些很难解决但确实非常重要的问题,他们只会回避这个问题并使用其他方法来达到目的,这显然不是一种很好的解决办法。

+
+

关于this

    +
  • this关键字是JavaScript中最复杂的机制之一

    +
  • +
  • this指向的两大误区:

    +
      +
    • 指向函数自身
    • +
    • 指向函数的作用域(在任何情况下this都不会指向函数的词法作用域,因为作用域“对象”是存在于JavaScript的引擎内部的)
    • +
    +
  • +
  • 当你想把this和词法作用域的查找混合使用时,这是无法实现的!

    +
  • +
  • 不能使用this来引用一个词法作用域内部的属性

    +
  • +
  • this实际上是在函数被调用时发生的绑定,this指向什么只取决于函数在哪被调用,和函数的生命位置毫无关系。

    +
  • +
+

this全面解析

    +
  • 要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置,最重要的就是分析调用栈(为了到达当前执行位置所调用的所有函数,即函数调用链)。

    +
  • +
  • 调用位置如何决定this的绑定对象?可顺序应用下面四条规则来判断

    +
      +
    • 是否在new中调用(new绑定)?绑定到新创建的对象。var bar = new foo()
    • +
    • 是否通过call、apply(显示绑定)或者bind(硬绑定)调用?绑定到指定的对象。var bar = foo.(obj2)
    • +
    • 是否在某个上下文对象(隐式绑定)中调用?绑定到那个上下文对象。var bar = obj1.foo()
    • +
    • 如果都不是即独立函数调用(默认绑定),严格模式下绑定到undefined,非严格模式下绑定到全局对象。var bar = foo()
    • +
    +
  • +
  • 默认绑定:直接使用不带任何修饰的函数引用进行调用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function foo() {
    console.log(this.a)
    }
    var obj = {
    a: 2,
    foo: foo
    }
    var bar = obj.foo // 函数别名
    var a = 'oops, global' // a是全局对象的属性
    bar() // >> 'oops, global'

    // 虽然bar是obj.foo的一个引用,看似是隐式绑定,但实际上,它引用的是foo的函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
    +
  • +
  • 隐式绑定:调用位置是否有上下文对象,或者说是否被某个对象拥有或包含。即我们必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接(隐式)绑定到这个对象上。

    +
    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
    // 参数传递就是一种隐式赋值
    function foo() {
    console.log(this.a)
    }
    function doFoo(fn) {
    fn() // fn其实引用的是foo函数本身
    }
    var obj = {
    a: 2,
    foo: foo
    }
    var a = 'oops, global' // a是全局对象的属性
    doFoo(obj.foo) // >> 'oops, global'

    // 如果把函数传入语言内置的函数而不是自定义的函数,结果是一样的
    function foo() {
    console.log(this.a)
    }
    var obj = {
    a: 2,
    foo: foo
    }
    var a = 'oops, global' // a是全局对象的属性
    setTimeout(obj.foo, 100) // >> 'oops, global'

    // JavaScript环境中内置的setTimeout()函数实现和下面伪代码类似
    function setTimeout(fn, delay) {
    // 等待delay秒
    fn() // 调用函数内容本身
    }
    +
  • +
  • 显示绑定:在对象内部不包含函数引用的情况下,在某个对象上强制调用函数,可以使用call(...)apply(...)方法,它们的第一个参数是一个对象,它们会吧这个对象绑定到this上,接着在调用函数时指定这this。

    +
    1
    2
    3
    4
    5
    6
    7
    function foo() {
    console.log(this.a)
    }
    var obj = {
    a: 2
    }
    foo.call(obj) // >> 2
    +
  • +
  • new绑定:JavaScript中的new机制实际上和面向类的语言完全不同,JavaScript中的new构造函数,其实只是一些使用new操作符调用的普通函数。它们不会属于某个类,也不会实例化一个类。

    +
  • +
  • 有些调用可能在无意中使用默认绑定规则。为了保护全局对象,可以使用一个DMZ对象(DemilitarizedZone,非军事区),即一个空的非委托的对象,比如const ∅ = Object.create(null),使用变量名不仅让函数变得更加安全,而且可以提高代码的可读性,因为标识“我希望this是空”这比null的含义更清楚。Object.create(null){}很像,但并不会创建Object.prototype这个委托,所以它比{}“更空”。

    +
  • +
  • ES6中箭头函数()=>{}并不会使用这四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。这其实和ES6之前的self = this机制一样。

    +
  • +
  • 代码风格:

    +
      +
    • 词法作用域风格:只使用词法作用域,并完全抛弃错误的this风格。
    • +
    • this风格:完全采用this风格,必要时使用bind(...),尽量避免使用self = this箭头函数()=>{}
    • +
    +
  • +
+

对象

混合对象“类”

原型

行为委托

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/07/18/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%EF%BC%88%E4%B8%8A%E5%8D%B7%EF%BC%89/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/08/12/2020\345\211\215\347\253\257\351\235\242\350\257\225/index.html" "b/2020/08/12/2020\345\211\215\347\253\257\351\235\242\350\257\225/index.html" new file mode 100644 index 000000000..49ffc92fc --- /dev/null +++ "b/2020/08/12/2020\345\211\215\347\253\257\351\235\242\350\257\225/index.html" @@ -0,0 +1,2304 @@ +2020前端面试 | 淳淳同学的个人博客 + + + + + + + + + + + + +

2020前端面试

+

今日裸辞,在投简历的过程中抽出一周集中复习前端知识,并做了此文以记录知识点。

+
+

目 录

[TOC]

+ +

自我介绍

    +
  • 学历
  • +
  • 项目经验
  • +
  • 技术栈 + + +
  • +
+ +

项目难点

    +
  • vue-print-nb:在windows系统上,低版本的浏览器调用#print打印会出现空白页,换成windows.print()即可;或者让用户更新浏览器
  • +
  • vue-print-nb进行单据打印:不同的系统、不同的浏览器,打印效果是不一样的,比如windows系统下element的table-column的props传入的字符串中英文单词超过长度会变成…而不会换行,换成scope就没事了
  • +
  • vue-fallcalender:日历组件,api介绍模糊,无法实现指定日期的初始化、无法对传入数据进行排序
  • +
  • 自定义el-form-item组件:利用slots与props实现自定义样式
  • +
  • 封装SimpleClass:每次页面都要loading、axios、赋值、closeLoading,及处理查询参数
  • +
  • 封装多文件上传Modal:限制文件大小、类型、个数,默认覆盖上传
  • +
  • 封装基于xlsx的Excel导入导出Modal:支持复杂表头的Excel导出 + + +
  • +
+ +

CSS

    +
  • CSS选择器
    查看答案
      +
    • *:通用元素选择器,匹配任何元素
    • +
    • E:标签选择器,匹配所有使用E标签的元素
    • +
    • info:class选择器,匹配所有class属性中包含info的元素
    • +
    • #footer:id选择器,匹配所有id属性等于footer的元素
    • +
    • E,F:多元素选择器,同时匹配所有E元素或F元素,E和F之间用逗号分隔
    • +
    • E F:后代元素选择器,匹配所有属于E元素后代的F元素,E和F之间用空格分隔
    • +
    • E > F:子元素选择器,匹配所有E元素的子元素F
    • +
    • E + F:毗邻元素选择器,匹配所有紧随E元素之后的同级元素F
    • +
    • E[att=val]:匹配所有att属性等于”val”的E元素
    • +
    • E:first-child:匹配父元素的第一个子元素
    • +
    • E:last-child:匹配父元素的最后一个子元素,等同于:nth-last-child(1)
    • +
    • E:nth-child(n):匹配其父元素的第n个子元素,第一个编号为1
    • +
    +
  • +
  • CSS 布局 position 详解
    查看答案
      +
    • fixed:固定定位,参照整个窗口
    • +
    • absolute:绝对定位,参照最近定位父元素
    • +
    • relative:相对定位,参照父级元素的原始点
    • +
    • static:默认值,没有定位,出现在正常的文档流中
    • +
    • sticky:吸附定位、磁铁定位、粘性定位。常和fixed结合制造吸附效果
    • +
    • inherit:集成父元素的定位
    • +
    +
  • +
  • css盒模型两种以及切换方式
  • +
  • 实现三列布局
    查看答案
      +
    • float触发BFC块级布局:float: left;、float: right;、overflow:hidden;
    • +
    • flex布局:display:flex;、flex:none;、flex:1;、flex:none;
    • +
    • table布局:display:table;、display:table-cell;
    • +
    • css计算宽度布局:float:left;、width:calc(100% - 100px);、float:right;
    • +
    +
  • +
  • Flex 布局教程:语法篇
  • +
  • Flex 布局示例
    查看答案
      +
    • 容器的属性
    • +
    • flex-direction属性:决定主轴的方向(即项目的排列方向)。.box { flex-direction: row | row-reverse | column | column-reverse; }
    • +
    • flex-wrap属性:默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。.box{ flex-wrap: nowrap | wrap | wrap-reverse; }
    • +
    • flex-flow属性:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。.box { flex-flow: <flex-direction> || <flex-wrap>; }
    • +
    • justify-content属性:定义了项目在主轴上的对齐方式。.box { justify-content: flex-start | flex-end | center | space-between | space-around; }
    • +
    • align-items属性:定义项目在交叉轴上如何对齐。.box { align-items: flex-start | flex-end | center | baseline | stretch; }
    • +
    • align-content属性:定义了多根轴线(多行)的对齐方式。如果项目只有一根轴线,该属性不起作用。.box { align-content: flex-start | flex-end | center | space-between | space-around | stretch; }
    • +
    • 项目的属性
    • +
    • order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0
    • +
    • flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
    • +
    • flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
    • +
    • flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小
    • +
    • flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
    • +
    • align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
    • +
    +
  • +
  • 实现两栏布局的几种方式
  • +
  • 页面两栏布局(变式)
  • +
  • 浮动元素引起的问题和清除浮动的办法
    查看答案
      +
    • 额外标签法:div style="clear:both;"></div
    • +
    • 使用after伪元素:#parent:after{content:".";height:0;visibility:hidden;display:block;clear:both;}
    • +
    • 设置 overflow 为 hidden 或者 auto
    • +
    • 父元素也设置浮动:(缺点)使得与父元素相邻的元素的布局会受到影响,不可能一直浮动到body,不推荐使用
    • +
    +
  • +
  • 清除浮动overflow:hidden的原理,为什么可以清除
    查看答案
      +
    • 通过触发BFC方式,实现清除浮动
    • +
    • 缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素
    • +
    +
  • +
  • CSS伪类-MDN
    查看答案
      +
    • active、focus、hover、link、visited
    • +
    • first-child、last-、nth-child()
    • +
    • first-of-type、last-of-、nth-of-type()
    • +
    • before、after
    • +
    • checked、not()、read-only
    • +
    +
  • +
  • div水平垂直居中的六种方法
  • +
  • 实现一个宽度自适应100%,宽高比16:9的div水平垂直居中
    查看答案
    1
    2
    3
    4
    5
    <div class="box">
    <div class="scale">
    <img src="http://img17.3lian.com/201612/16/88dc7fcc74be4e24f1e0bacbd8bef48d.jpg" class="item"/>
    </div>
    </div>
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    .box {
    width: 80%;
    }

    .scale {
    width: 100%;
    padding-bottom: 56.25%;
    height: 0;
    position: relative;
    }

    .item {
    width: 100%;
    height: 100%;
    background-color: aquamarine;
    position: absolute;
    }
  • +
  • transform有哪些用法
    查看答案
      +
    • 旋转:rotate(transform: rotate(45deg);
    • +
    • 缩放:scale(transform: scale(0.5, 2);
    • +
    • 移动:translate(transform: skew(45px, 150px);
    • +
    • 倾斜:skew(transform: skew(30deg, 30deg);
    • +
    • 基准点:transform-origin(transform-origin: 10px 10px;
    • +
    • 用法:transform: rotate(45deg) scale(0.5) skew(30deg, 30deg) translate(100px, 100px);这四种变形方法顺序可以随意,但不同的顺序导致变形结果不同,原因是变形的顺序是从左到右依次进行
    • +
    +
  • +
  • CSS哪些属性脱离文档流
    查看答案
      +
    • 第一种:定位
        +
      • position:absolute
      • +
      • position:fixed
      • +
      +
    • +
    • 第二种:浮动
        +
      • float:left
      • +
      • float:right
      • +
      +
    • +
    • 注:position:sticky,就是粘性定位并不会脱离文档流
    • +
    +
  • +
  • css表达式:expression
    查看答案
      +
    • IE5及其以后版本支持在CSS中使用expression,用来把CSS属性和Javascript脚本关联起来
    • +
    • 每两小时刷新一次背景色:background-color:expression((new Date().getHours()%2?”#B8D4FF”:”#F08A00”));
    • +
    • 依照浏览器的大小来安置一个元素的位置:left: expression(document.body.offsetWidth - 180 “px”);
    • +
    +
  • +
  • html5/css3响应式页面开发总结
    查看答案
      +
    • 自适应是一套模板适应所有终端,但每种设备上看到的版式是一样的,俗称宽度自适应。
    • +
    • 响应式一套模板适应所有终端,但每种设备看到的版式可以是不一样的。如:http://segmentfault.com/
    • +
    • 设置meta标签
        +
      • +
      • §width=device-width:宽度等于当前设备的宽度
      • +
      • §initial-scale:初始的缩放比例(默认设置为1.0)
      • +
      • §minimum-scale:允许用户缩放到的最小比例(默认设置为1.0)
      • +
      • §maximum-scale:允许用户缩放到的最大比例(默认设置为1.0)
      • +
      • §user-scalable:用户是否可以手动缩放(默认设置为no,因为我们不希望用户放大缩小页面)
      • +
      +
    • +
    • css3的媒体查询
        +
      • @media (orientation:portrait) and (max-width:460px) {}
      • +
      • orientation:portrait(指定输出设备中的页面可见区域高度大于或等于宽度) | landscape
      • +
      • +
      +
    • +
    • 百分比布局
    • +
    +
  • +
  • 对BFC的规范的理解
    查看答案
      +
    • W3CCSS2.1规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用)
    • +
    • BFC,块级格式化上下文,一个创建了新的BFC的盒子是独立布局的,盒子里面的子元素的样式不会影响到外面的元素
    • +
    • 在同一个BFC中的两个毗邻的块级盒在垂直方向(和布局方向有关系)的margin会发生折叠
    • +
    +
  • +
  • 触发BFC的条件
    查看答案
      +
    • 浮动元素:float除none以外的值,float、right
    • +
    • 绝对定位元素:position(fixed、absolute)
    • +
    • display:inline-blocks、table-cells、table-captions
    • +
    • overflow除了visible以外的值:hidden、auto、scroll
    • +
    +
  • +
  • BFC的用处
    查看答案
      +
    • 可以阻止边距折叠(margin collapsing)
    • +
    • 可以包含内部元素的浮动
    • +
    • 可以阻止元素被浮动覆盖
    • +
    +
  • +
  • display有哪些值?说明他们的作用。
    查看答案
      +
    • none:隐藏,此元素不会被显示
    • +
    • block:块显示;此元素将显示为块级元素,此元素前后会带有换行符
    • +
    • inline:内嵌,默认。此元素会被显示为内联元素,元素前后没有换行符
    • +
    • table:表格显示,此元素会作为块级表格来显示(类似table标签),表格前后带有换行符
    • +
    • inline-block:元素既具有block元素可以设置宽高的特性,同时又具有inline元素默认不换行的特性
    • +
    • list-item:象块类型元素一样显示,并添加样式列表标记
    • +
    • inherit:规定应该从父元素继承display属性的值
    • +
    +
  • +
  • display中block、inline和inline-block的区别
    查看答案
      +
    • block
    • +
    • 前后都有换行,和前后元素都不在一行
    • +
    • 宽度、行高、边距都可以自行设置
    • +
    • 宽度缺省是它的容器的100%,除非设置一个宽度
    • +
    • div,p,h1,form,ul和li是块元素的例子
    • +
    • inline
    • +
    • 和其他元素都在一行上
    • +
    • 高,行高及顶和底边距不可改变
    • +
    • 宽度就是它的文字或图片的宽度,不可改变
    • +
    • span,a,label,input,img,strong和em是inline元素的例子
    • +
    • inline-block
    • +
    • 将对象呈递为内联对象,但是对象的内容作为块对象呈递。旁边的内联对象会被呈递在同一行内,允许空格。(应用此特性的元素呈现为内联对象,周围元素保持在同一行,但可以设置宽度和高度地块元素的属性)
    • +
    +
  • +
  • display:none和visibility:hidden的区别
    查看答案
    + + + + + + + + + + + + + + + + + + + + + + +
    特性\标签display:nonevisibility:hidden
    空间占据不占据任何空间,它各边的元素会合拢元素空间依旧存在
    回流与渲染会产生reflow和repaint(回流与重绘)没有这个影响前端性能的问题
    株连性其子孙节点元素全部不可见子元素默认不显示,但如果子孙元素应用了visibility:visible就可以展现出来
    +
  • +
  • css隐藏页面元素的多种方法
    查看答案
      +
    • 常见的CSS预处理器:
        +
      • Sass
      • +
      • Less
      • +
      • Stylus
      • +
      • PostCSS:模块化工具,速度快3-30倍,功能强大(既不是预处理器也不是后处理器
      • +
      +
    • +
    +
  • +
  • sass常用属性
  • +
  • less简介
  • +
  • 谈谈PostCSS
  • +
  • 纯CSS画的基本图形(圆形、三角形、多边形、爱心、八卦等)
  • +
  • 纯CSS绘制三角形(各种角度) + + +
  • +
+ +

JavaScript

    +
  1. 作用域

    +
    查看答案
      +
    • 作用域是一种规则,用于确定引擎在何处以及如何根据标识符名称进行变量查找(引擎、编译器、作用域)
    • +
    • 在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量或抵达最外层作用域(即全局作用域)为止。查找也分为两种:LHS和RHS
    • +
    +
  2. +
  3. 作用域的两种工作模型

    +
    查看答案
      +
    • ==词法作用域==:在写代码或定义时确定的,作用域链基于作用域的嵌套,即更关注在何处声明。大多数编程语言都采用
    • +
    • 动态作用域:在运行时确定的,作用域链是基于调用栈的,即更关注函数是从何处调用的。this的机制也是如此(像但不是)
    • +
    +
  4. +
  5. 要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置,最重要的就是分析调用栈(为了到达当前执行位置所调用的所有函数,即函数调用链)。

    +
  6. +
  7. 调用位置如何决定this的绑定对象?可顺序应用下面四条规则来判断

    +
    查看答案
      +
    • 是否在new中调用(new绑定)?绑定到新创建的对象。var bar = new foo()
    • +
    • 是否通过call、apply(显示绑定)或者bind(硬绑定)调用?绑定到指定的对象。var bar = foo.call(obj2)
    • +
    • 是否在某个上下文对象(隐式绑定)中调用?绑定到那个上下文对象。var bar = obj1.foo()
    • +
    • 如果都不是即独立函数调用(默认绑定),严格模式下绑定到undefined,非严格模式下绑定到全局对象。var bar = foo()
    • +
    +
  8. +
  9. ES6中箭头函数()=>{}并不会使用这四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。这其实和ES6之前的self = this机制一样。

    +
  10. +
  11. 代码风格:

    +
    查看答案
      +
    • 词法作用域风格:只使用词法作用域,并完全抛弃错误的this风格。
    • +
    • this风格:完全采用this风格,必要时使用bind(...),尽量避免使用self = this箭头函数()=>{}
    • +
    +
  12. +
  13. ==原型==

    +
    查看答案
      +
    • 所有的函数都有一个特殊的属性:prototype(原型),prototype属性是一个指针,指向的是一个对象(原型对象),原型对象中的方法和属性都可以被函数的实例所共享。所谓的函数实例是指以函数作为构造函数创建的对象,这些对象实例都可以共享构造函数的原型的方法。(所有函数的默认原型都是Object的实例)
    • +
    +
  14. +
  15. ==作用域链和原型链==

    +
    查看答案
      +
    • 作用域链:在词法作用域中寻找标识符&变量。作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
    • +
    • 原型链:在原型对象中寻找引用类型的属性
    • +
    +
  16. +
  17. ==闭包==

    +
  18. +
  19. 闭包是基于词法作用域书写代码时所产生的自然结果

    +
  20. +
  21. 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使==函数是在当前词法作用域之外执行==

    +
  22. +
  23. 无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包

    +
  24. +
  25. 闭包的应用场景:

    +
    查看答案
      +
    • function调用setTimeout(...)(延迟函数的回调会在循环结束时才执行,即使定时器是setTimeout(..., 0)
    • +
    • 定时器、事件监听器、Ajax请求、跨窗口通信、WebWorkers、任何异步或同步任务,只要使用了==回调函数==,实际上就是在使用闭包
    • +
    • for循环
    • +
    • 立即执行函数表达式IIFE:(function(){...})())(本身创建了闭包,但严格来说并不是闭包)
    • +
    • ==模块模式==:比如jQuery和$符就是jQuery模块的公共api
    • +
    +
  26. +
  27. 以上均来自《你不知道的JavaScript(上卷)》

    +
  28. +
  29. js的原型和原型链

    +
    查看答案
      +
    • __proto__指向其构造函数的prototype原型,即
    • +
    • person1.__proto__,指向Person.prototype(相等)
    • +
    • Person.prototype,指向Object.prototype
    • +
    • Object.prototype,指向Null
    • +
    • 上述即原型链
    • +
    +
  30. +
  31. 一篇文章看懂_proto_和prototype的关系及区别

    +
  32. +
  33. prototype、__proto__与constructor区别与联系

    +
    查看答案
      +
    • function Person() {}
    • +
    • Person.prototype.name = ‘Lee’
    • +
    • Person.prototype // {name: Lee, constructor: ƒ}
    • +
    • Person.constructor // [Function: Function] || ƒ Function() { [native code] }
    • +
    • Person.proto // {name: Lee, constructor: ƒ}[Function] || ƒ () { [native code] }
    • +
    • —我是一条分割线—
    • +
    • var person1 = new Person()
    • +
    • person1.prototype // undefined
    • +
    • person1.constructor // [Function: Person] || ƒ Person() {}
    • +
    • person1.__proto__ // {name: Lee, constructor: ƒ}
    • +
    • person1.constructor.prototype // {name: Lee, constructor: ƒ}
    • +
    • person1.constructor.prototype === person1.proto // true
    • +
    +
  34. +
  35. 浅析JavaScript中原型及constructor、__proto__、prototype的关系

    +
    查看答案
      +
    • 任意对象:拥有constructor,指向构造函数
    • +
    • 任意对象:拥有__proto__,指向构造函数的原型
    • +
    • 任意函数:拥有prototype,指向原型对象
    • +
    • 任意函数:拥有__proto__和constructor,因为函数也是一种对象,所以这是从它的原型即Object构造函数那里共享来的
    • +
    +
  36. +
  37. 浅拷贝和深拷贝的区别

    +
    查看答案
      +
    • 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
    • +
    • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
    • +
    +
  38. +
  39. 深拷贝的思路(递归?利用Object.prototype.toString.call(obj)判断类型,链接同上)

    +
    查看答案
      +
    • 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
    • +
    +
  40. +
  41. 实现深拷贝的方法

    +
    查看答案
      +
    • JSON.parse(JSON.stringfy(obj))
        +
      • 缺点1:拷贝Date()类型会被转换为字符串
      • +
      • 缺点2:RegExp、Error对象,结果将只得到空对象{}
      • +
      • 缺点3:function、undefined,结果会丢失
      • +
      • 缺点4:NaN、Infinity和-Infinity,结果会变成null
      • +
      • 缺点5:JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的,则使用JSON.parse(JSON.stringify(obj))深拷贝后,结果会丢弃对象的constructor
      • +
      +
    • +
    • 递归遍历逐级浅拷贝
        +
      • 每一次递归,无论此函数是否有改变都需要重新递归
      • +
      +
    • +
    • proxy深拷贝
        +
      • 判断target类型,isPlainObject()
      • +
      • const isProxy = value => !!value && !!value[MY_IMMER]
      • +
      • 利用上述语句判断是否是我们的proxy类型与对暗号,如果不是proxy,我们需要将其拦截
      • +
      • 判断我们深拷贝的对象是否有改变
      • +
      +
    • +
    +
  42. +
  43. 函数声明和var声明的优先级

    +
  44. +
  45. 如何利用正则匹配字符串

    +
    查看答案
      +
    • exec:查找并返回当前的匹配结果,并以数组的形式返回;exec是RegExp对象的方法;每次只返回一次匹配项
    • +
    • match:math是String对象的方法;一次性返回所有匹配项
    • +
    +
  46. +
  47. 将字符串变成数字

    +
    查看答案
      +
    • 一、当字符串中是纯数字,var s = ‘234’
    • +
    • s *= 1,字符串在运算操作中会被当做数字类型来处理
    • +
    • string的两个转换函数,只对string有效。parseInt(s)parseFloat(s)
    • +
    • Number(s),强制类型转换
    • +
    • 二、当字符串是数字加字母等非数字,如var s = ‘234string’,只能用第二种方法parseIntparseFloat
    • +
    +
  48. +
  49. new Set的数组去重和自己实现的哪个性能会更好

    +
  50. +
  51. new操作符的作用及运行过程(new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例)

    +
    查看答案
      +
    • 一个继承自 Person.prototype 的新对象被创建。
    • +
    • 使用指定的参数调用构造函数 Person ,并将 this 绑定到新创建的对象。new Person 等同于 new Person(),也就是没有指定参数列表,Person 不带任何参数调用的情况。
    • +
    • 由构造函数返回的对象就是new表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
    • +
    • 简单回答
    • +
    • 创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。
    • +
    • 属性和方法被加入到this引用的对象中。
    • +
    • 新创建的对象由this所引用,并且最后隐式的返回this。
    • +
    +
  52. +
  53. Instanceof运算符的作用及运行过程(用于测试构造函数的prototype属性,是否出现在对象的原型链中的任何位置)

    +
    查看答案
      +
    • 判断构造函数的原型对象(如Person.prototype和Object.prototype)是否在实例对象(person1)的原型链上(__proto__);如果在对象的原型链上,就返回true,如果不在就返回false;
    • +
    +
  54. +
  55. 变量提升是什么?Var、Const 、let 是如何变量提升的(暂时性死区)?

    +
  56. +
  57. var与let、const的区别

    +
    查看答案
      +
    • var声明变量存在变量提升,let和const不存在变量提升
    • +
    • let、const都是块级局部变量
    • +
    • 同一作用域下let和const不能声明同名变量,而var可以
    • +
    +
  58. +
  59. js检测变量是否已定义

    +
    查看答案
      +
    • console.log(typeof undefined); // undefined
    • +
    • console.log(typeof null); // object
    • +
    • console.log(typeof 123); // number
    • +
    • console.log(typeof “字符串”); // string
    • +
    • console.log(typeof {}); // object
    • +
    • console.log(typeof aa); // undefined
    • +
    +
  60. +
  61. js判断对象中是否有某属性的常用方法

    +
    查看答案
      +
    • 点(.x)或者方括号([x]):这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。局限性:不能用在x的属性值存在,但可能为undefined的场景
    • +
    • in运算符:如果指定的属性在指定的对象或其原型链中,则in运算符返回true。局限性:无法区分自身和原型链上的属性,即无法判断该属性是否是自身的
    • +
    • hasOwnProperty():适用于只判断自身属性的场景。
    • +
    +
  62. +
  63. ES6用过的新特性

    +
    查看答案
      +
    • 箭头函数、字符串插值、const、let(块作用域)
    • +
    • Promises异步机制、模块import导入、模块export导出
    • +
    • function默认参数、class类定义与extend继承
    • +
    • for-of遍历对象、…展开操作符
    • +
    +
  64. +
  65. es6中的数组方法

    +
    查看答案
      +
    • 扩展运算符(…)
    • +
    • Array.from()
    • +
    • Array.of()
    • +
    • 数组实例的 copyWithin()
    • +
    • 数组实例的 find() 和 findIndex()
    • +
    • 数组实例的 fill()
    • +
    • 数组实例的 entries(),keys() 和 values()
    • +
    • 数组实例的 includes()
    • +
    • 数组实例的 flat(),flatMap()
    • +
    • 数组的空位
    • +
    • Array.prototype.sort()的排序稳定性
    • +
    +
  66. +
  67. - es6中的对象方法

    +
    查看答案
      +
    • 属性的简洁表示法:module.exports = { getItem, setItem, clear };
    • +
    • 属性名表达式:obj[‘a’ + ‘bc’] = 123;
    • +
    • 方法的name属性
    • +
    • 属性的可枚举性和遍历
        +
      • 有四个操作会忽略enumerable为false的属性
      • +
      • for…in循环:只遍历对象自身的和继承的可枚举的属性。
      • +
      • Object.keys():返回对象自身的所有可枚举的属性的键名。
      • +
      • JSON.stringify():只串行化对象自身的可枚举的属性。
      • +
      • Object.assign():忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
      • +
      +
    • +
    • super关键字:指向当前对象的原型对象。
        +
      • super.foo等同于Object.getPrototypeOf(this).foo(属性)Object.getPrototypeOf(this).foo.call(this)(方法)
      • +
      +
    • +
    • 对象的扩展运算符:let { x, y, …z } = { x: 1, y: 2, a: 3, b: 4 };
    • +
    • 链判断运算符
        +
      • obj?.prop // 对象属性
      • +
      • obj?.[expr] // 同上
      • +
      • func?.(…args) // 函数或对象方法的调用
      • +
      +
    • +
    • Null 判断运算符
        +
      • const animationDuration = response.settings?.animationDuration ?? 300;
      • +
      +
    • +
    +
  68. +
  69. - JS继承的实现方式

    +
    查看答案
      +
    • 原型链继承:将父类的实例作为子类的原型。function Cat(){}; Cat.prototype = new Animal()
    • +
    • 构造继承:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)。function Cat(name){Animal.call(this);this.name = name || 'Tom';}
    • +
    • 实例集成:为父类实例添加新特性,作为子类实例返回。function Cat(name){var instance = new Animal();instance.name = name || 'Tom';return instance;}
    • +
    • 拷贝继承
    • +
    • 组合继承
    • +
    • 寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
    • +
    +
  70. +
  71. - es6中的对象新增方法

    +
    查看答案
      +
    • Object.is():Object.is('foo', 'foo')Object.is({}, {})
        +
      • ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
      • +
      +
    • +
    • Object.assign(target, source1, source2)
        +
      • 用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
      • +
      +
    • +
    • Object.getOwnPropertyDescriptors(obj)
        +
      • 返回指定对象所有自身属性(非继承属性)的描述对象(descriptor)
      • +
      • {foo:{configurable:true,enumerable:true,value:123,writable:true}}
      • +
      +
    • +
    • __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
        +
      • Object.getPrototypeOf(person1)===person1.constructor.prototype
      • +
      +
    • +
    • Object.keys(),Object.values(),Object.entries()
    • +
    • Object.fromEntries()
        +
      • 是Object.entries()的逆操作,用于将一个键值对数组转为对象
      • +
      +
    • +
    +
  72. +
  73. - js es6遍历对象的6种方法(应用中推荐前三种)

    +
    查看答案
      +
    • Object.keys()、Object.values()、Object.entries():返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)
    • +
    • for…in…:循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
    • +
    • Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(包括不可枚举属性,但不含Symbol属性)
    • +
    • Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有属性(不管属性名是Symbol或字符串,也不管是否可枚举)
    • +
    +
  74. +
  75. - for…in…遍历对象时,为什么有的对象属性可以被遍历到,有的不行?

    +
    查看答案
      +
    • js中基本包装类型的自带原型属性是不可枚举的,如Object, Array, Number等
    • +
    • Object.defineProperty()定义的enumerable:false的属性也是不可枚举的
    • +
    +
  76. +
  77. - for-in和for-of的区别

    +
    查看答案
      +
    • for…in循环出的是key值,for…of循环出的是value值
    • +
    • 推荐在循环对象属性的时候,使用for…in,在遍历数组的时候的时候使用for…of
    • +
    • for…of不能循环普通的对象,需要通过和Object.keys()搭配使用
    • +
    +
  78. +
  79. - for-of的工作过程

    +
    查看答案
      +
    • 首先调用集合的Symbol.iterator方法,返回一个新的迭代器对象(迭代器对象可以是任意具有.next()方法的对象)
    • +
    • for-of循环将重复调用这个.next()方法,每次循环调用一次
    • +
    +
  80. +
  81. - a.b.c()中c的this(idea自己实现)

    +
    查看答案
    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
    var x = '我是a'
    var a = {
    x: '我是a',
    b: {
    x: '我是b',
    c: function () {
    var x = '我是c'
    console.log(this)
    }
    }
    }
    a.b.c() // { x: '我是b', c: [Function: c] }
    var d = {
    x: '我是d',
    e: () => a.b.c()
    }
    d.e() // { x: '我是b', c: [Function: c] }

    // 如果把c改为箭头函数
    c: () => {
    var x = '我是c'
    console.log(this)
    }
    // 输出结果如下
    a.b.c() // Window {...}
    d.e() // Window {...}
  82. +
  83. forEach和for/for…of循环在使用await上的区别,如果想同时执行呢?(promise.all)

    +
    查看答案
      +
    • for/for…of:遍历到每个元素后,执行await后的方法
    • +
    • forEach:在遍历每个元素后,执行的是该方法接收的回调函数方法,然后在回调中,执行await方法,forEach方法内部调用回调函数时,并没有使用await修饰,所以回调方法并不会等待上一个回调执行完毕。所以,内部的await也就失去了意义。
    • +
    +
  84. +
  85. IIEF立即执行为什么用两对括号包裹

    +
    查看答案
      +
    • 模拟函数执行
    • +
    • (function () { return 233 })() // 233
    • +
    • (function () { return 233 }()) // 233
    • +
    • +function () { return 233 }() // 233
    • +
    • -function () { return 233 }() // -233
    • +
    • ~function () { return 233 }() // -233
    • +
    • !function () { return 233 }() // false
    • +
    +
  86. +
  87. []+{}{}+[]结果有何不同

    +
    查看答案
      +
    • 加法会进行隐式类型转换,规则是调用其valueOf()或toString()以取得一个非对象的值(primitive value)
    • +
    • []+{} 结果是 “[object Object]”,字符串串接
    • +
    • {}+[] 结果是 0,{}解析为{ // empty block },即对一个空数组执行正号运算,实际上就是把数组转型为数字
    • +
    • ({}+[]) 结果是 “[object Object]”
    • +
    • +[] 结果是 0 ,此处+为正号
    • +
    • +{} 结果是 NaN ,此处+为正号
    • +
    • [].toString() 结果是 “”
    • +
    • ({}).toString() 结果是 “[object Object]”
    • +
    • {}.toString():会出错,Uncaught SyntaxError: Unexpected token .
    • +
    • 0+[] 结果是 “0”
    • +
    • 0+{} 结果是 “0[object Object]”
    • +
    +
  88. +
  89. var a=b=c=d=5是什么意思?如果接下来再写一句,d=9,a,b,c的值会变化吗?

    +
    查看答案
      +
    • 初始化给a、b、c、d赋值都为5
    • +
    • 不改变,改变d后a、b、c值不会改变,因为a、b、c、d都是值类型的变量,各自的值存在于自己的栈当中,当d变化了其他栈中的值不改变。
    • +
    +
  90. +
  91. var a=b=c=d=【1,2,3,4,5】是什么意思?如果接下来写一句d【5】=9;a,b,c,的值会发生变化吗

    +
    查看答案
      +
    • 发生变化,a、b、c、d值都改为[1,2,3,4,5,9]
    • +
    • 因为a、b、c、d是引用类型,引用类型的数据存在于堆当中,栈中存的是指向堆的地址,初始化时 a、b、c、d在各自的栈中指向的堆是同一个,该堆保存着[1,2,3,4,5],当改变了堆中的值,其他对象跟着改变。
    • +
    +
  92. +
  93. var a=b=c=d=【1,2,3,4,5】是什么意思?如果接下来再写一句d=【9】;a,b,c的值会发生变化吗

    +
    查看答案
      +
    • 不改变,因为对于d来说改变的是d栈中的地址,此时d指向的堆已经不是原地址,所以此时d与其他几个对象的值已经不同了。
    • +
    • 考点:对比上一题,改变的是对应堆中的值,而此题是将d栈中的指针地址改变了。
    • +
    +
  94. +
  95. js表达式和语句的区别

    +
    查看答案
      +
    • 表达式是由运算符构成,并运算产生结果的语法结构。每个表达式都会产生一个值,它可以放在任何需要一个值的地方,比如作为一个函数调用的参数
    • +
    • 语句:则是由“;(分号)”分隔的句子或命令。如果在表达式后面加上一个“;”分隔符,这就被称为“表达式语句”。它表明“只有表达式,而没有其他语法元素的语句”
        +
      • 声明语句:变量声明和函数声明
      • +
      • 赋值语句
      • +
      • 控制语句:能够对语句执行顺序产生改变,包括条件语句和循环语句,当然还有比较特殊的标签语句
      • +
      • 表达式语句:这些语句去掉最后分号,都也可当表达式用的。常见的有:对象操作(new、delete)、函数调用(函数执行,必有返回值)等
      • +
      +
    • +
    +
  96. +
  97. async 和 await 是谁的语法糖

    +
  98. +
  99. generator 又是如何实现的?

    +
  100. +
  101. js的5种基本和1(2)种引用数据类型

    +
    查看答案
      +
    • 基本数据类型 :String、Number、Boolean 、Null、Undefined、Symbol、BigInt
    • +
    • 引用数据类型:Object(Funtion)
    • +
    • 判断方法:Object.prototype.toString.call(data)
    • +
    +
  102. +
  103. null与undefined知识点(链接同上)

    +
    查看答案
      +
    • undefined值派生自null,所以相等比较null==undefined为true
    • +
    • null是空指针但未用,有个占位符,undefined连指针都没有,也没有占位符,所以绝对比较null===undefined为false
    • +
    +
  104. +
  105. 堆和栈的区别(第二篇帖子)

    +
    查看答案
      +
    • 栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的
    • +
    • 堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
    • +
    +
  106. +
  107. 为什么会有跨域的问题?

    +
    查看答案
      +
    • 前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。在请求的过程中我们要想回去数据一般都是post/get请求,所以跨域问题出现
    • +
    • 跨域问题来源于JavaScript的同源策略,即只有协议+主机名(域名)+端口号(如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。
    • +
    • 跨域问题是针对JS和ajax的,html本身没有跨域问题(比如a标签、script标签、甚至form标签,只要是带src的)可以直接跨域发送数据并接收数据
    • +
    +
  108. +
  109. 九种跨域方式实现原理

    +
    查看答案
      +
    • 开发环境:webpack自带proxyTable:target:'http://192.168.0.123:8080/'、pathRewrite:{'^/api': '/api'}
    • +
    • 生产环境:利用nginx反向代理,将请求分发到部署到相应项目的服务器上:location ~ /api { proxy_pass http://127.0.0.1:23480; }
    • +
    • JSONP:ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加。(但JSONP只支持GET请求,不支持POST请求)jsonp原理详解
    • +
    • CORS:是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),CORS通信与同源的AJAX通信没有差别,就是在请求头信息中增加了Origin:http://192.168.0.123:8080/字段。详见阮一峰的博客
    • +
    • web sockets:在JS创建了webSocket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。只有在支持webSocket协议的服务器上才能正常工作。(http->ws; https->wss)
    • +
    +
  110. +
  111. JavaScript手动实现JSONP代码

    +
    查看答案
      +
    • document.createElement(‘script’)
    • +
    • jsonpScript.setAttribute(‘src’, ${url}callback=${callbackFunction})
    • +
    • (完成后)document.getElementsByTagName(‘head’)[0].removeChild(script)
    • +
    +
  112. +
  113. 对前端模块化的认识

    +
    查看答案
      +
    • CommonJS规范:允许模块通过require方法来同步加载(同步意味阻塞)所要依赖的其他模块,然后通过module.exports来导出需要暴露的接口。CommonJS是以在浏览器环境之外构建JavaScript生态系统为目标而产生的项目,比如在服务器和桌面环境中。
    • +
    • AMD标准:AMD是RequireJS在推广过程中对模块定义的规范化产出(异步模块定义)。
        +
      • AMD标准中定义了以下两个API:
      • +
      • require([module], callback);用来加载一系列模块
      • +
      • define(id, [depends], callback);用来定义并暴露一个模块
      • +
      • 优点:
      • +
      • 异步加载,提前执行
      • +
      • 适合在浏览器环境中加载模块,且可以并行加载多个模块
      • +
      +
    • +
    • CMD标准:CMD是SeaJS在推广过程中对模块定义的规范化产出(在CommomJS和AMD基础上提出)。
        +
      • define(function (requie, exports, module) {});
      • +
      • 优点:
      • +
      • 依赖就近,延迟执行
      • +
      • 可以很容易在服务器中运行
      • +
      +
    • +
    • ES6模块化:EcmaScript6标准增加了JavaScript语言层面的模块体系定义(关键字)。在 ES6 中,我们使用import关键字引用模块,使用export关键字来导出模块。
    • +
    +
  114. +
  115. 四种常见的JS模块化管理方法的比较(表格形式:导入语法、导出语法、加载方式等)

    +
  116. +
  117. AMD 和 CMD 的区别

    +
    查看答案
      +
    • 对于依赖的模块,AMD是提前执行,CMD是延迟执行。
    • +
    • AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require。
    • +
    • AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一
    • +
    +
  118. +
  119. ES6 模块与 CommonJS 模块的差异

    +
    查看答案
      +
    • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    • +
    • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
    • +
    • 即 CommonJS 先加载整个模块,输出一个对象,取对象内相应的值,输出后内部不会再变化;ES6 是静态编译命令,先加载一个引用,等执行时再根据引用到加载模块内取值输出,动态引用不缓存。
    • +
    • 目前很少JS引擎能直接支持 ES6 标准,因此 Babel 的做法实际上是将不被支持的 import 翻译成目前已被支持的 require。
    • +
    +
  120. +
  121. import和require的区别

    +
    查看答案
      +
    • 遵循规范
        +
      • require 是 AMD规范引入方式
      • +
      • import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法
      • +
      +
    • +
    • 调用时间
        +
      • require是运行时调用,所以require理论上可以运用在代码的任何地方
      • +
      • import是编译时调用,所以必须放在文件开头
      • +
      +
    • +
    • 本质
        +
      • require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量
      • +
      • import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require
      • +
      +
    • +
    +
  122. +
  123. XML和JSON的区别?

    +
  124. +
  125. 什么是dom0,dom1,dom2,dom3

    +
    查看答案
      +
    • DOM0:不是W3C规范。
    • +
    • DOM1:开始是W3C规范。专注于HTML文档和XML文档。
    • +
    • DOM2:对DOM1增加了样式表对象模型
    • +
    • DOM3:对DOM2增加了内容模型(DTD、Schemas) 和文档验证。
    • +
    +
  126. +
  127. dom0级和dom2级的区别

    +
    查看答案
      +
    • DOM0级事件处理:简单且具有跨浏览器的优势;但函数会被覆盖,只会执行最后一个的函数
    • +
    • DOM2级事件处理:不会覆盖且顺序执行;不具有跨浏览器优势
    • +
    +
  128. +
  129. 事件,事件冒泡和事件捕获

    +
  130. +
  131. addEventListener的第三个参数到底该怎么设置

    +
    查看答案
      +
    • target.addEventListener(type, listener[, useCapture]);
    • +
    • useCapture: 默认值为false(即 使用事件冒泡)
    • +
    +
  132. +
  133. JS阻止冒泡和取消默认事件(默认行为)

    +
    查看答案
      +
    • @click.stop="func($event)"vue取消事件冒泡
    • +
    • @click.prevent="func($event)"vue阻止默认事件
    • +
    • event.stopPropagation()W3C提供的方法,起到阻止捕获和冒泡阶段中当前事件的进一步传播
    • +
    • event.preventDefault()可以取消默认事件
    • +
    • e.cancelBubble = true,只针对IE
    • +
    +
  134. +
  135. Javascript垃圾回收方法

    +
    查看答案
      +
    • 标记清除(mark and sweep)是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
    • +
    • 引用计数(reference counting) 是另一种不太常见的垃圾回收策略。在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
    • +
    • 在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用问题。
    • +
    +
  136. +
  137. Web Worker

    +
    查看答案
      +
    • 作用就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker线程在后台运行,两者互不干扰。等到Worker线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被Worker线程负担了,主线程(通常负责UI交互)就会很流畅,不会被阻塞或拖慢。
    • +
    • 通过 worker = new Worker(url) 加载一个JS文件来创建一个worker,同时返回一个worker实例。
    • +
    • 通过worker.postMessage( data) 方法来向worker发送数据。
    • +
    • 绑定worker.onmessage方法来接收worker发送过来的数据。
    • +
    • 可以使用 worker.terminate() 来终止一个worker的执行。
    • +
    +
  138. +
  139. js按位运算符

    +
    查看答案
      +
    • & 按位与
    • +
    • | 按位或
    • +
    • ^ 按位异或
    • +
    • ~ 按位非
    • +
    • >> 右移
    • +
    • << 左移
    • +
    +
  140. +
  141. 常见的js位运算

    +
    查看答案
    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
    33
    34
    35
    36
    37
    一、取整
    // 异或运算取整::位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。
    // 所以,将一个小数与0进行或运算,等同于对该数去除小数部分,即取整数位。
    12.9 ^ 0 // 12
    -12.9 ^ 0 // -12

    // 双否定位操作符取整
    ~~4.9 // 4
    ~~(-4.9) // -4

    // 左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效。
    13.5 << 0 // 13
    -13.5 << 0 // -13

    // 或运算取整:位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。
    // 所以,将一个小数与0进行或运算,等同于对该数去除小数部分,即取整数位。
    2.9 | 0 // 2
    -2.9 | 0 // -2

    二、判断奇数、偶数
    function assert(n) {
    if (n & 1) {
    console.log("n是奇数");
    } else {
    console.log("n是偶数");
    }
    }

    & 是按位与运算
    1 & 1 = 1
    1 & 0 = 0
    0 & 0 = 0
    0 & 1 = 0
    int型变量在一般内存中占用4个字节
    2 = 00000000 00000000 00000000 0000010
    1 = 00000000 00000000 00000000 0000001
    2 & 1 = 0
  142. +
  143. 2..toString

    +
  144. +
  145. 为什么0.1+0.2不等于0.3?

    +
    查看答案
      +
    • 原因在于在JS中采用的IEEE_754的双精度标准,计算机内部存储数据的编码的时候,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1。
    • +
    • 两个有舍入误差的值在求和时,相互抵消了,但这种“负负得正,相互抵消”不一定是可靠的,当这两个数字是用不同长度数位来表示的浮点数时,舍入误差可能不会相互抵消。
    • +
    • 又如,对于0.1+0.3,结果其实并不是0.4,但0.4是最接近真实结果的数,比其它任何浮点数都更接近。许多语言也就直接显示结果为0.4了,而不展示一个浮点数的真实结果了。
    • +
    +
  146. +
  147. 数组的降维5种办法

    +
    查看答案
      +
    • 数组字符串化:arr += ''; arr = arr.split(',');
    • +
    • 递归,判断后push或concat
    • +
    • Array.prototype.flat()
    • +
    • 使用reduce、concat和递归无限反嵌套多层嵌套的数组
    • +
    +
  148. +
  149. 数组的join:把数组中的所有元素拼接成一个字符串,元素是通过指定的分隔符进行分隔的。

    +
  150. +
  151. 字符串的split:把一个字符串通过指定的分隔符分割成字符串数组。

    +
  152. +
  153. 数组的map和forEach的区别

    +
    查看答案
      +
    • map()方法:创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来,所以可以使用复合(composition)(map(),filter(),reduce()等组合使用)来玩出更多的花样
    • +
    • foreEach()方法:针对每一个元素执行提供的函数,不返回新数组,返回值为undefined
    • +
    +
  154. +
  155. js如何判断数据类型

    +
    查看答案
      +
    • Object.prototype.toString.call(data):最全面,但需要注意区分大小写
    • +
    • typeof操作符:不适合用于判断是否为数组。当使用typeof判断数组和对象的时候,都会返回object。可以使用isArray()来判断是否为数组
    • +
    • instanceof:只能用来判断对象和函数,不能用来判断字符串和数字等。判断它是否为字符串和数字时,只会返回false
    • +
    • constructor:返回对创建此对象的数组函数的引用
    • +
    +
  156. +
  157. ES6中Promise.all和Promise.race区别

    +
    查看答案
      +
    • Promise.all:都成功才会调用success,如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。
    • +
    • Promise.race:只要有一个成功就会调用success,但是进程不会立即停止
    • +
    • 对promise的考察,then链的应用
    • +
    +
  158. +
  159. Document.cookie-MDN

    +
  160. +
  161. js如何操作cookie

    +
    查看答案
      +
    • set:document.cookie = key + “=” + val + “;expires=” + date.toGMTString();
    • +
    • get:document.cookie.replace(/[ ]/g, “”).split(“;”)
    • +
    • delete:document.cookie = key + “=v; expires =” + date.toGMTString();
    • +
    • 或 利用小框架:cookies.js
    • +
    +
  162. +
  163. Cookie和Session的使用和区别

    +
  164. +
  165. js进程(process)和线程(thread)的区别

    +
    查看答案
      +
    • 进程:操作系统分配的占有CPU资源的最小单位。拥有独立的地址空间。
    • +
    • 线程:安排CPU执行的最小单位。同一个进程下的所有线程,共享进程的地址空间。
    • +
    +
  166. +
  167. js进程(process)和线程(thread)的关系

    +
    查看答案
      +
    • 以多进程形式,允许多个任务同时运行
    • +
    • 以多线程形式,允许单个任务分成不同的部分运行
    • +
    • 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源
    • +
    • 以下是详细解释:
    • +
    • 进程它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态
    • +
    • 一个进程可以包括多个线程
    • +
    • 一个进程的内存空间是共享的,每个线程都可以使用这些共享内存
    • +
    • 一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存
    • +
    • 一个防止其他线程使用的简单方法"互斥锁"(Mutual exclusion,缩写Mutex),防止多个线程同时读写某一块内存区域
    • +
    +
  168. +
  169. Object.create实现了什么?传null得到的结果和普通对象有什么区别?

    +
    查看答案
      +
    • 创建对象的方式不同
        +
      • newObject():通过构造函数来创建对象, 添加的属性是在自身实例下。console.log(b)//{rep:"apple"} console.log(b.__proto__)//{}
      • +
      • Object.create():创建对象的另一种方式,可以理解为继承一个对象,添加的属性是在原型下。console.log(b)//{} console.log(b.__proto__)//{rep:"apple"}
      • +
      +
    • +
    • 创建对象属性的性质不同,Object.getOwnPropertyDescriptors(obj)
        +
      • newObject():{value:42,writable:true,enumerable:true,configurable:true}
      • +
      • Object.create():{value:42,writable:false,enumerable:false,configurable:false}
      • +
      +
    • +
    • 创建空对象时不同
        +
      • newObject():__proto:Object
      • +
      • Object.create():No properties
      • +
      +
    • +
    • __proto__属性
        +
      • newObject():?
      • +
      • Object.create():?
      • +
      +
    • +
    +
  170. +
  171. call、apply和bind有何区别

    +
    查看答案
      +
    • call对函数直接调用:xw.say.call(xh,"实验小学","六年级")
    • +
    • apply对函数直接调用:xw.say.apply(xh,["实验小学","六年级"])
    • +
    • bind返回的是一个函数,需要手动()调用,且可在调用时传参:xw.say.bind(xh,"实验小学","六年级")()xw.say.bind(xh)("实验小学","六年级")
    • +
    +
  172. +
  173. 手写实现call、apply、bind

    +
    查看答案

    模拟call

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Function.prototype.myCall = function(obj){
    if(obj === null || obj === undefined){
    obj = window;
    } else {
    obj = Object(obj);
    }
    let arg = [];
    let val ;
    for(let i = 1 ; i<arguments.length ; i++){
    arg.push( 'arguments[' + i + ']' ) ;
    }
    obj._fn_ = this;
    val = eval( 'obj._fn_(' + arg + ')' )
    delete obj._fn_;
    return val
    }
    + +

    模拟apply

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Function.prototype.myApply = function(obj,arr){
    if(obj === null || obj === undefined){
    obj = window;
    } else {
    obj = Object(obj);
    }
    let args = [];
    let val ;
    for(let i = 0 ; i<arr.length ; i++){
    args.push( 'arr[' + i + ']' ) ;
    }
    obj._fn_ = this;
    val = eval( 'obj._fn_(' + args + ')' )
    delete obj._fn_;
    return val
    }
    + +

    模拟bind

    +
    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
    Function.prototype.myFind = function(obj){
    if(obj === null || obj === undefined){
    obj = window;
    } else {
    obj = Object(obj);
    }
    let _this = this;
    let argArr = [];
    let arg1 = [];
    for(let i = 1 ; i<arguments.length ; i++){
    arg1.push( arguments[i] );
    argArr.push( 'arg1[' + (i - 1) + ']' ) ;
    }
    return function(){
    let val ;
    for(let i = 0 ; i<arguments.length ; i++){
    argArr.push( 'arguments[' + i + ']' ) ;
    }
    obj._fn_ = _this;
    console.log(argArr);
    val = eval( 'obj._fn_(' + argArr + ')' ) ;
    delete obj._fn_;
    return val
    };
    }
  174. +
  175. call、apply、bind的应用

    +
    查看答案

    求数组中的最大和最小值

    +
    1
    2
    3
    var arr = [1,2,3,89,46]
    var max = Math.max.apply(null,arr)//89
    var min = Math.min.apply(null,arr)//1
    + +

    将类数组转化为数组

    +
    1
    var trueArr = Array.prototype.slice.call(arrayLike)
    + +

    数组追加

    +
    1
    2
    3
    4
    5
    var arr1 = [1,2,3];
    var arr2 = [4,5,6];
    var total = [].push.apply(arr1, arr2);//6
    // arr1 [1, 2, 3, 4, 5, 6]
    // arr2 [4,5,6]
    + +

    判断变量类型

    +
    1
    2
    3
    4
    5
    function isArray(obj){
    return Object.prototype.toString.call(obj) == '[object Array]';
    }
    isArray([]) // true
    isArray('dot') // false
    + +

    利用call和apply做继承

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Person(name,age){
    // 这里的this都指向实例
    this.name = name
    this.age = age
    this.sayAge = function(){
    console.log(this.age)
    }
    }
    function Female(){
    Person.apply(this,arguments)//将父元素所有方法在这里执行一遍就继承了
    }
    var dot = new Female('Dot',2)
    + +

    使用 log 代理 console.log

    +
    1
    2
    3
    4
    function log(){
    console.log.apply(console, arguments);
    }
    // 当然也有更方便的 var log = console.log()
  176. +
  177. 替代es6中拓展运算符传参的方式

    +
    查看答案
      +
    • 函数调用中替代数组的apply方法
    • +
    • add(...args)等同于f.apply(null, args)
    • +
    +
  178. +
  179. 实现一个发布订阅,有订阅(on),发布(emit),一次订阅功能(once)

    +
  180. +
  181. 实现防抖节流

    +
    查看答案

    防抖(debounce):(最后一次)规定一个期限时间,在首次触发事件时,不立即执行回调函数,而是在期限时间后再执行,如果期限时间内回调函数被重复执行,则期限时间重新计时。(输入完字符串再发请求)

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /* 
    *fn --> 需要防抖的函数;
    *delaytime --> 毫秒数,防抖所需期限值;
    */
    function debounce(fn,delaytime){
    let timer = null
    return function(){
    if(timer){
    clearTimeout(timer) //进入这里说明当前存在一个执行过程,并且同时又执行了一个相同事件,故取消当前的执行过程
    }
    timer = setTimeout(fn,delaytime)
    }
    }
    function show_scrollPosition(){
    var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop;
    console.log("当前滚动条位置为:",scrollPosition);
    }

    window.onscroll = debounce(show_scrollPosition,1000)
    + +

    节流(throttle):(第一次)规定一个期限时间,在该时间内,触发事件的回调函数只能执行一次,如果期限时间内回调函数被多次触发,则只有一次能生效。(页面滚动到底部就加载更多)

    +
    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
    function throttle(fn, delay) {
    let last_time
    let timer = null
    return function () {
    let cur_time = new Date().getTime()
    if (last_time && cur_time < last_time + delay) { //若为真,则表示上次执行过,且在期限值范围内
    clearTimeout(timer)
    timer = setTimeout(() => {
    fn();
    last_time = cur_time
    }, delay)
    } else {
    last_time = cur_time;
    fn();
    }

    }
    }

    function show_scrollPosition() {
    var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop;
    console.log("当前滚动条位置为:", scrollPosition);
    }

    window.onscroll = throttle(show_scrollPosition, 1000)
    +
  182. +
  183. 实现请求并发限制,具体为:封装一个函数,传递请求并发的个数为参数,实现对并发请求的限制

    +
  184. +
  185. 说说闭包以及垃圾回收机制

    +
  186. +
  187. 利用async和await如何处理异常事件

    +
  188. +
  189. 箭头函数和普通函数有什么区别?如果想改变箭头函数中绑定this怎么办?

    +
  190. +
  191. 原生js判断鼠标在一个有对角线矩形的位置

    + + + +
  192. +
+ +

Vue 2.x

源码相关

    +
  1. BiliBili: Vue 2.x 源码解读(12) —— path阶段
  2. +
+
    +
  • Emm…,慢慢看
  • +
+
    +
  1. v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?

    +
    查看答案
      +
    • 源码: compiler/codegen/index.js
    • +
    • Vue 2.x版本中,v-if的优先级大于v-for
    • +
    • Vue 3.x版本中,v-for的优先级大于v-if
    • +
    • render函数: with(this){return _c('div', { ... })}
    • +
    +
    +
  2. +
  3. Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?

    +
    查看答案
      +
    • 源码: src\core\instance\state.js - initData()
    • +
    • Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的
    • +
    • 采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题
    • +
    • 而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况
    • +
    +
    +
  4. +
  5. 你知道vue中key的作用和工作原理吗?说说你对它的理解。

    +
    查看答案
      +
    • 源码: src\core\vdom\patch.js - updateChildren()
    • +
    • key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两 个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操 作量,提高性能
    • +
    • 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug(暂时未知)
    • +
    • vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果
    • +
    +
    +
  6. +
  7. 你怎么理解vue中的diff算法?

    +
    查看答案
      +
    • 源码1: 必要性,lifecycle.js - mountComponent()
        +
      • 组件中可能存在很多个data中的key使用
      • +
      +
    • +
    • 源码2: 执行方式,patch.js - patchVnode()
        +
      • patchVnode是diff发生的地方,整体策略: 深度优先,同层比较
      • +
      +
    • +
    • 源码3: 高效性,patch.js - updateChildren()
    • +
    • diff算法是虚拟DOM技术的必然产物: 通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上
    • +
    • 另外,也需要 diff 高效的执行对比过程,从而降低时间复杂度为O(n)
    • +
    • vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方
    • +
    • vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch
    • +
    • diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做首首、尾尾、首尾、尾首4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。
    • +
    +
    +
  8. +
  9. 谈一谈对vue组件化的理解?

    +
    查看答案
      +
    • 源码1: 组件定义,src\core\global-api\assets.js
        +
      • vue-loader会编译template为render函数,最终导出的依然是组件配置对象
      • +
      +
    • +
    • 源码2: 组件化优点,lifecycle.js - mountComponent()
        +
      • 组件、Watcher、渲染函数和更新函数之间的关系
      • +
      +
    • +
    • 源码3: 组件化实现: 构造函数,src\core\global-api\extend.js、实例化及挂载,src\core\vdom\patch.js - createElm()
    • +
    • 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
    • +
    • 组件化开发能大幅提高应用开发效率、测试性、复用性等;
    • +
    • 组件使用按分类有: 页面组件、业务组件、通用组件;
    • +
    • vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
    • +
    • vue中常见组件化技术有: 属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
    • +
    • 合理的划分组件,有助于提升应用性能;
    • +
    • 组件应该是高内聚、低耦合的;
    • +
    • 遵循单向数据流的原则。
    • +
    +
    +
  10. +
  11. 谈一谈对vue设计原则的理解?

    +
    查看答案
      +
    • 在vue的官网上写着大大的定义和特点: 渐进式JavaScript框架、易用、灵活和高效
    • +
    +
    +
  12. +
  13. 谈谈你对MVC、MVP和MVVM的理解?

    +
  14. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你了解哪些Vue性能优化方法?

    +
    查看答案
      +
    • 路由懒加载
    • +
    • keep-alive缓存页面
    • +
    • 使用v-show复用DOM
    • +
    • v-for 遍历避免同时使用 v-if
    • +
    • 长列表性能优化,静态列表:list = Object.freeze([])
    • +
    • 虚拟滚动:vxe-table
    • +
    • 事件的销毁,Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。beforeDestroy() { clearInterval(this.timer) }
    • +
    • 图片懒加载,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域 内的图片先不做加载,等到滚动到可视区域后再去加载。参考项目:vue-lazyload,代码:<img v-lazy="/static/img/1.png">
    • +
    • 第三方插件按需引入,import { Button, Select } from 'element-ui';
    • +
    • 无状态的组件标记为函数式组件,<template functional> ... </template>
    • +
    • 子组件分割,独立可复用功能可抽象出来
    • +
    • 变量本地化,如果有for循环等频繁访问this.xxx的情况,提前赋值给本地变量
    • +
    • SSR
    • +
    +
    +
  2. +
  3. 简单说一说vuex使用及其理解?

    +
    查看答案
      +
    • Vuex实现了一个单向数据流,在全局拥有一个state存放数据,当组件要更改state中的数据时,必须通过mutation提交修改信息,mutation同时提供了订阅者模式供外部插件调用获取state数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作 需要走action,但action也是无法直接修改state的,还是需要通过mutation来修改state的数据。最后,根据state的变化,渲染到视图上。
    • +
    +
    +
  4. +
  5. vue中组件之间的通信方式?

    +
    查看答案
      +
    • props ★★
        +
      • 父组件 A 通过 props 向子组件 B 传递值, B 组件传递 A 组件通过 $emit A 组件通过 v-on/@ 触发
      • +
      • 子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
      • +
      +
    • +
    • $emit/$on 事件总线 ★★ +
    • +
    • vuex ★★★
        +
      • 结合localStorage保存登录信息及权限列表等
      • +
      +
    • +
    • $parent/$children
    • +
    • $attrs/$listeners
        +
      • 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法。
      • +
      • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。通常配合 interitAttrs 选项一起使用。
      • +
      • $listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v- on=”$listeners” 传入内部组件
      • +
      +
    • +
    • provide/inject ★★★
        +
      • 优点:使用简单 缺点:不是响应式
      • +
      • 父级:provide: { name: '王者峡谷' //这种绑定是不可响应的 }name: this会有响应式,把当前组件实例传递下去,但子组件会绑定一些多余的属性,比如props、methonds等)
      • +
      • 子级:inject: ['name'] }
      • +
      +
    • +
    +
    +
  6. +
  7. vue-router 中的导航钩子由那些?

    +
  8. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 什么是递归组件?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 说一说vue响应式理解?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. vue如果想要扩展某个组件现有组件时怎么做?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. vue为什么要求组件模版只能有一个根元素?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. watch和computed的区别以及怎么选用?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你知道nextTick的原理吗?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你知道vue双向数据绑定的原理吗?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 简单说一说vue生命周期的理解?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. vue.js的两大核心
    查看答案
      +
    • 数据驱动,也就是数据的双向绑定
    • +
    • 组件系统
        +
      • 模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。
      • +
      • 初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。
      • +
      • 接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。
      • +
      • 方法(methods):对数据的改动操作一般都在组件的方法内进行。
      • +
      • 生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,最新2.0版本对于生命周期函数名称改动很大。
      • +
      • 私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
      • +
      +
    • +
    +
  2. +
  3. 什么是MVVM(操作数据,就是操作视图,就是操作DOM)
  4. +
  5. MVVM的优点:
    查看答案
      +
    • 分离视图(View) 和模型(Model) ,降低代码耦合,提高视图或者逻辑的重用性:比如视图(View) 可以独立于Model|变化和修改,-个ViewModel可以绑定不同的”View”上,当View变化的时候Model不可以不变,当Model|变化的时候View也可以不变。你可以把一些视图逻辑放在 -个ViewModel里面, 让很多view重用这段视图逻辑
    • +
    • 提高可测试性:ViewModel的存在可以帮助开发者更好地编写测试代码
    • +
    • 自动更新dom:利用双向绑定,数据更新后视图自动更新,让开发者从繁琐的手动dom中解放
    • +
    +
  6. +
  7. MVVM的缺点:
    查看答案
      +
    • Bug很难被调试:因为使用双向绑定的模式,当你看到界面异常了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的
    • +
    • 一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存
    • +
    • 对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高
    • +
    +
  8. +
  9. 你怎么理解vue中的diff算法?
    查看答案
      +
    • diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为0(n)。
    • +
    • vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方。
    • +
    • vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上-次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch。
    • +
    • diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。
    • +
    +
  10. +
  11. 谈一谈对vue组件化的理解?
    查看答案
      +
    • 组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
    • +
    • 组件化开发能大幅提高应用开发效率、测试性、复用性等;
    • +
    • 组件使用按分类有:页面组件、业务组件、通用组件;
    • +
    • vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
    • +
    • vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
    • +
    • 合理的划分组件,有助于提升应用性能;
    • +
    • 组件应该是高内聚、低耦合的;
    • +
    • 遵循单向数据流的原则。
    • +
    +
  12. +
  13. 谈一谈对vue设计原则的理解?
  14. +
  15. 谈谈你对MVC、MVP和MVVM的理解?
  16. +
  17. 你了解哪些Vue性能优化方法?
  18. +
  19. 什么叫发布订阅模式
  20. +
  21. 如何实现发布订阅模式
  22. +
  23. proxy和object.definepropoty的区别
  24. +
  25. proxy的应用场景
  26. +
  27. 你对虚拟dom和diff算法的理解,实现render函数
  28. +
  29. 绑定this的几种方式
  30. +
  31. setState是同步还是异步的
  32. +
+

双向绑定

    +
  1. 双向绑定的原理(差点让我手撸一个双向绑定)Object.defineProperty()Proxy
    查看答案
      +
    • Vue 采用数据劫持 结合 发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty(点我查看该属性)来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。
    • +
    • Observer:数据的观察者,让数据对象的读写操作都处于自己的监管之下。当初始化实例的时候,会递归遍历data,用Object.defineProperty来拦截数据(包含数组里的每个数据)。
    • +
    • Dep:数据更新的发布者,get数据的时候,收集订阅者,触发Watcher的依赖收集;set数据时发布更新,通知Watcher 。
    • +
    • Watcher:数据更新的订阅者,订阅的数据改变时执行相应的回调函数(更新视图或表达式的值)。
    • +
    • 图中红色的箭头表示的是收集依赖时获取数据的流程。Watcher会收集依赖的时候(这个时机可能是实例创建时, 解析模板、初始化watch、初始化computed,也可能是数据改变后,Watcher执行回调函数前),会获取数据的值,此时Observer会拦截数据 (即调用get函数),然后通知Dep可以收集订阅者啦。Dep将订阅数据的Watcher保存下来,便于后面通知更新。
    • +
    • 图中绿色的箭头表示的是数据改变时,发布更新的流程。当数据改变时,即设置数据时,此时Observer会拦截数据(即调用set函数),然后通知Dep,数据改变了,此时Dep通知Watcher,可以更新视图啦。
    • +
    +
  2. +
  3. vue2中对于数组的变化检测是重写数组方法:
    查看答案
      +
    • ‘push’
    • +
    • ‘pop’
    • +
    • ‘shift’
    • +
    • ‘unshift’
    • +
    • ‘splice’
    • +
    • ‘sort’
    • +
    • ‘reverse’
    • +
    +
  4. +
  5. 你知道vue中key的作用和工作原理吗?说说你对它的理解。
    查看答案
      +
    • 源码中找答案:src\core\vdom\patch.js - updateChildren()
    • +
    • key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中调用updateChildren(),利用patchVNode()方法,通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量提高性能。
    • +
    • 如果不适用key的时候,在执行patchVNode()之前会一直判断sameVNode()并返回true,因为不设置key的话标签的key会默认为undefined,此时undefined===undefined,所以会判断为同一个节点。
    • +
    • 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug
    • +
    • vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
    • +
    +
  6. +
  7. 你是如何理解Vue的响应式系统的?
    查看答案
      +
    • 任何一个Vue Component都有一个与之对应的Watcher实例。
    • +
    • Vue的data上的属性会被添加getter和setter属性。
    • +
    • 当Vue Component render函数被执行的时候,data. 上会被触碰(touch),即被读,getter方法会被调用,此时Vue会去记录此Vuecomponent所依赖的所有data。(这-过程被称为依赖收集)
    • +
    • data被改动时(主要是用户操作),即被写,setter方法会被调用,此时Vue会去通知所有依赖于此data的组件去调用他们的render函数进行更新。
    • +
    +
  8. +
  9. 既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虛拟DOM进行diff检测差异?
    查看答案
      +
    • 考点: Vue的变化侦测原理
    • +
    • 前置知识:依赖收集、虚拟DOM、响应式系统
    • +
    • 现代前端框架有两种方式侦测变化,-种是pull-种是push
    • +
    • push: Vue的响应式系统则是push的代表当Vue程序初始化的时候就会对数据data进行依赖的收集,-但数据发生变化,响应式系统就会立刻得知,因此Vue是-开始就知道是「在哪发生变化了」,但是这又会产生一一个问题,如果你熟悉Vue的响应式系统就知道通常一个绑定-个数据就需要一个Watcher,-但我们的绑定细粒度过高就会产生大量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择中等细粒度的方案,在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom Diff 获取更加具体的差异,而Virtual Dom Diff 则是pull操作,Vue是push+pull结合的方式进行变化侦测的.
    • +
    • pull:其代表为React,我们可以回忆一下React是如何侦测到变化的,我们通常会用setStateAPI显式更新,然后React会进行一层层的Virtual Dom Diff操作找出差异然后Patch到DOM上,React从一-开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的iff操作查找「哪发生变化了」,另外-个代表就是Angular的脏检查操作。
    • +
    +
    + +
  10. +
+

生命周期

    +
  1. 如何从零开始初始化vue项目
  2. +
  3. 生命周期是什么
    查看答案
      +
    • Vue实例有一个完整的生命周期,也就是从
    • +
    • 开始创建、初始化数据、编译模版、挂载Dom->渲染、更新-> 渲染、卸载等-系列过程,我们称这是Vue的生命周期。
    • +
    +
  4. +
  5. 简单说一说vue生命周期的理解?
    查看答案
      +
    • beforeCreate:在实例初始化之后,数据观测(data observe)和event/watcher事件配置之前被调用,这时无法访问data及props等数据;
    • +
    • created:在实例创建完成后被立即调用,此时实例已完成数据观测(data observer),属性和方法 的运算,watch/event事件回调,挂载阶段还没开始, $el 尚不可用。
    • +
    • beforemount:在挂载开始之前被调用,相关的render函数首次被调用。
    • +
    • mounted:实例被挂载后调用,这时el被新创建的vm. $el 替换,若根实例挂载到了文档上的元素上,当mounted被调用时vm.$el也在文档内。注意mounted不会保证所有子组件一起挂载。
    • +
    • beforeupdata:数据更新时调用,发生在虚拟dom打补丁前,这时适合在更新前访问现有dom,如手动移除已添加的事件监听器。
    • +
    • updated:在数据变更导致的虚拟dom重新渲染和打补丁后,调用该钩子。当这个钩子被调用时,组件dom已更新,可执行依赖于dom的操作。多数情况下应在此期间更改状态。 如需改变,最好使用watcher或计算属性取代。注意updated不会保证所有的子组件都能一起被重绘。
    • +
    • beforedestory:在实例销毁之前调用。在这时,实例仍可用。
    • +
    • destroyed:实例销毁后调用,这时vue实例的所有指令都被解绑,所有事件监听器被移除,所有子实 例也被销毁。
    • +
    • activated:(新增钩子)keep-alive 组件激活时调用。 类似 created 没有真正创建,只是激活
    • +
    • deactivated:(新增钩子)keep-alive 组件停用时调用。类似 destroyed 没有真正移除,只是禁用
    • +
    +
  6. +
  7. Vue.js的八大生命周期
  8. +
  9. Vue.js的父子组件生命周期调用顺序
  10. +
  11. Vue.js的ajax请求放在哪个生命周期中
  12. +
  13. 你知道nextTick的原理吗?
    查看答案
      +
    • (唯一能监听到DOM改动的API:MutationObserver,HTML5新增的属性,用于监听DOM修改事件,能够监听到节点的属性、文本内容、子节点等的改动,是一个功能强大的利器。)
    • +
    • 事件循环(Event Loop),浏览器
    • +
    • 任务队列(task queues)
    • +
    • 微任务队列(MicroTask_Queue),是做队列控制的最佳选择。每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。在执行microtask的过程中后加入microtask队列的微任务,也会在下一次事件循环之前被执行。也就是说,macrotask总要等到microtask都执行完后才能执行,microtask有着更高的优先级。
    • +
    • 常见的microtask有:Promise、MutationObserver、Object.observe(废弃),以及nodejs中的 process.nextTick.
    • +
    • vue的降级策略,队列控制的最佳选择是microtask,而microtask的最佳选择是Promise。但如果当前环境不支持Promise,vue就不得不降级为macrotask来做队列控制了。
    • +
    • 在vue2.5的源码中,macrotask降级的方案依次是:setImmediate、MessageChannel、setTimeout.
    • +
    +
    + +
  14. +
+

data

    +
  1. Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?
    查看答案
      +
    • 源码中找答案:src\core\instance\state.js - initData()
    • +
    • Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,即单例存在,不需要担心这种情况。
    • +
    +
  2. +
  3. 如果改了data的多个值update钩子会执行几次,为什么
  4. +
+

computed && watch

    +
  1. Vue computed 的实现原理
  2. +
  3. Vue watch 的实现原理
  4. +
  5. computed和watch的区别和应用
  6. +
+

vuex

    +
  1. vuex是什么vuex的源码、底层(Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue)、使用场景(组件通信、登录状态、加入购物车、公共码表维护、页面缓存(慎用))
  2. +
  3. mutation的用法
  4. +
+

vue-router

    +
  1. Vue-router 的实现原理
  2. +
  3. vue的两种路由模式,问我这两种路由模式的底层实现(昨天刚复习过 美滋滋)
  4. +
  5. vue-router中push和replace的区别
  6. +
  7. vueRouter的钩子函数
      +
    • 全局的路由钩子函数:beforeEach(全局前置守卫)、afterEach(全局后置守卫)
    • +
    • 路由配置文件独享的钩子函数:beforeEnter
    • +
    • 组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate
    • +
    +
  8. +
  9. vue-router中的导航钩子由那些?
      +
    • 全局的路由钩子函数:beforeEach(全局前置守卫)、afterEach(全局后置守卫)
    • +
    • 路由配置文件独享的钩子函数:beforeEnter
    • +
    • 组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate
    • +
    +
  10. +
  11. 如何(动态)控制路有权限,颗粒多大
  12. +
  13. vue项目实现路由按需加载(路由懒加载)的3种方式
      +
    • vue的异步组件技术:component: resolve => require(['@/components/home'],resolve)
    • +
    • es提案的import():const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')。有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用命名chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
    • +
    • webpack提供的require.ensure():r => require.ensure([], () => r(require('@/components/index')), 'demo')
    • +
    +
  14. +
  15. 完整的导航解析流程
      +
    • 导航被触发。
    • +
    • 在失活的组件里调用离开守卫。
    • +
    • 调用全局的 beforeEach 守卫。
    • +
    • 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
    • +
    • 在路由配置里调用 beforeEnter。
    • +
    • 解析异步路由组件。
    • +
    • 在被激活的组件里调用 beforeRouteEnter。
    • +
    • 调用全局的 beforeResolve 守卫 (2.5+)。
    • +
    • 导航被确认。
    • +
    • 调用全局的 afterEach 钩子。
    • +
    • 触发 DOM 更新。
    • +
    • 用创建好的实例调用 beforeRouteEnter - 守卫中传给 next 的回调函数。
    • +
    +
  16. +
+

组件通信

    +
  1. Vue.js组件如何通信以及有哪些方式?
  2. +
  3. vue中如何实现兄弟组件通信:
      +
    • EventBus(事件总线),即一个新的vue实例。
    • +
    • 使用方法:export const eventBuss = new Vue(),然后event.$emit()调用,created中使用event.$on()监听
    • +
    +
  4. +
  5. vue如果想要扩展某个组件现有组件时怎么做?
      +
    • mixin或者slots
    • +
    +
  6. +
  7. 什么是递归组件?
      +
    • 组件自身调用自身,但一定要有终止条件
    • +
    +
  8. +
+

directives

    +
  1. v-on事件修饰符及按键修饰符
      +
    • .stop:阻止事件冒泡
    • +
    • .self:当事件在该元素本身触发时才触发事件
    • +
    • .capture:添加事件侦听器是,使用事件捕获模式
    • +
    • .prevent:阻止默认事件
    • +
    • .once:事件只触发一次
    • +
    • 下面为,按键修饰符
    • +
    • @click.stop.prevent="btnClick":既阻止了默认事件,又阻止了事件冒泡
    • +
    • @keyup.enter.native="handleRegister"@keyup.13="submit":监听回车事件
    • +
    +
  2. +
  3. v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?
      +
    • 源码中找答案compiler/codegen/index.js
    • +
    • 显然v-for优先于v-if被解析(把你是怎么知道的告诉面试官,比如打印渲染函数console.log(app.$options.render);或源码 else if 判断先v-for再v-if)
    • +
    • 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能
    • +
    • 要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环
    • +
    • 如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项
    • +
    +
  4. +
  5. vue自定义指令和私有指令
  6. +
  7. delete和Vue.delete(this.$delete)删除数组的区别
      +
    • var a=[1,2,3,4]
    • +
    • var b=[1,2,3,4]
    • +
    • delete a[1]
    • +
    • console.log(a) // [1,empty,2,3]
    • +
    • this.$delete(b,1)
    • +
    • console.log(b) // [1,3,4]
    • +
    +
  8. +
  9. v-model的作用和原理
  10. +
  11. v-show和v-if的区别,与dom操作是否相关
  12. +
  13. 如何取到子组件的实例,除了$ref还有无别的方法
  14. +
+

others

    +
  1. vue中css处scoped的实现原理及穿透的用法
  2. +
  3. video的层级(他的层级是最高的,问我怎么在video上添加一些div,我之前有看过aliplayer这个插件,面试官也很满意)
  4. +
  5. 30行写一个Vue图片懒加载指令
  6. +
+ + + + +

Vue 3.x

    +
  1. vue3.x的新特性研究
  2. +
+
    +
  • Emm…,慢慢看
  • +
+
    +
  1. 你对Vue3.0的新特性有没有了解?
  2. +
  3. vue2和vue3的特点和新特性
  4. +
  5. Proxy与Object.defineProperty的优劣对比?
      +
    • Proxy的优势如”下:
        +
      • Proxy可以直接监听对象而非属性
      • +
      • Proxy可以直接监 听数组的变化
      • +
      • Proxy有多达13种拦截方法,不限于apply、ownKeys、 deleteProperty、 has等 等是object . defineProperty不具备的
      • +
      • Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而object.defineProperty只能遍历对象属性直接修改
      • +
      • Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
      • +
      +
    • +
    • Object.defineProperty的优势如”下:
        +
      • 兼容性好,支持IE9 + + +
      • +
      +
    • +
    +
  6. +
+ +

Webpack

    +
  • webpack基础配置
      +
    • context:基础目录,绝对路径,用于从配置中解析入口起点(entry point)和 loader
    • +
    • entry:起点或是应用程序的起点入口。从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。
    • +
    • output:位于对象最顶级键(key),包括了一组选项,指示webpack如何去输出、以及在哪里输出你的「bundle、asset和其他你所打包或使用webpack载入的任何内容」。比如path: config.build.assetsRoot
    • +
    • resolve:extensions: [‘.js’, ‘.vue’, ‘.json’],自动解析确定的扩展。
    • +
    • 模式(mode):告知 webpack 使用相应模式的内置优化。development、production
    • +
    +
  • +
  • webpack的工作原理和流程
      +
    • Webpack CLI 启动打包流程;
    • +
    • 载入 Webpack 核心模块,创建 Compiler 对象;
    • +
    • 使用 Compiler 对象开始编译整个项目;
    • +
    • 从入口文件开始,解析模块依赖,形成依赖关系树;
    • +
    • 递归依赖树,将每个模块交给对应的 Loader 处理;
    • +
    • 合并 Loader 处理完的结果,将打包结果输出到 dist 目录。
    • +
    +
  • +
  • webpack的构建流程
      +
    • 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数。
    • +
    • 开始编译:用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始编译整个项目。
    • +
    • 确定入口:根据配置中的entry找出所有的入口文件。
    • +
    • 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
    • +
    • 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系树
    • +
    • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
    • +
    • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统即dist目录。
    • +
    +
  • +
  • webpack基础知识
  • +
  • wepack中loader和plugin的区别
      +
    • 功能作用的角度区分:
        +
      • loader:直译为“加载器”,用于加载某些非js资源文件。这让webpack有了加载和解析非js文件的能力。因为webpack本身只能打包commonjs规范的js文件,对于其他资源例如css,图片,或者其他的语法集,比如jsx,coffee,是没有办法加载的。这就需要对应的loader将资源转化,加载进来。从字面意思,也能看出,loader是用于加载的,它作用于一个个文件上。
      • +
      • plugin:直译为“插件”,用于扩展webpack的功能。它直接作用于webpack,扩展了它的功能。在webpack的运行生命周期中会广播许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。
      • +
      +
    • +
    • 运行时机的角度区分:
        +
      • loader:运行在打包文件之前(loader为在模块加载时的预处理文件)
      • +
      • plugins:在整个编译周期都起作用。
      • +
      +
    • +
    • 配置方法的角度区分:
        +
      • loader:在module.rules中配置,也就是说他作为模块的解析规则存在。类型为数组,每一项都是一个object,里面描述了对于什么类型的文件(test),使用了什么加载(loader)和参数(options)
      • +
      • plugins:在plugins中单独配置。类型为数组,每一项都是一个plugin的实例,参数他通过构造函数传入。
      • +
      +
    • +
    +
  • +
  • 常见的、用过的loader
      +
    • 文件:file-loader将文件发送到输出文件夹,并返回(相对)URL,png|jpg|gif、svg、mp4
    • +
    • JSON:json-loader加载 JSON 文件(默认包含)
    • +
    • 转换编译(Transpiling):script-loader在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析
    • +
    • 转换编译(Transpiling):babel-loader加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
    • +
    • 模板(Templating):html-loader导出 HTML 为字符串,需要引用静态资源
    • +
    • 样式:css-loader解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
    • +
    • 样式:style-loader将模块的导出作为样式添加到 DOM 中
    • +
    • 样式:less-loader加载和转译 LESS 文件
    • +
    • 样式:sass-loader加载和转译 SASS/SCSS 文件
    • +
    • 清理和测试(Linting && Testing):eslint-loader,PreLoader,使用 ESLint 清理代码
    • +
    • 框架(Frameworks):vue-loader加载和转译 Vue 组件
    • +
    +
  • +
  • 常见的、用过的plugin
      +
    • DefinePlugin允许创建一个在编译时可以配置的全局常量(’process.env’: require(‘../config/dev.env’))
    • +
    • WebpackBar显示打包/启动可视化进度条
    • +
    • mini-css-extract-plugin是可以提取CSS到单独的文件中
    • +
    • HtmlWebpackPlugin简化了HTML文件的创建
    • +
    • webpack.HotModuleReplacementPlugin()启用热替换模块插件
    • +
    • CopyWebpackPlugin将单个文件或整个目录复制到生成目录(from:path.resolve(__dirname, ‘../static’),to:config.build.assetsSubDirectory,ignore: [‘.*’])
    • +
    +
  • +
  • 谈谈你对webpack的看法
      +
    • WebPack 是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、JavaScript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源。
    • +
    +
  • +
  • webpack的两大特色:
      +
    • code splitting(代码拆分)按需加载
    • +
    • ==loader==:可以处理各种类型的静态文件,并且支持串联操作
    • +
    +
  • +
  • webpack是以commonJS的形式来书写脚本滴,但对AMD/CMD的支持也很全面,方便旧项目进行代码迁移。
  • +
  • webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:
      +
    • 对CommonJS、AMD、ES6的语法做了兼容
    • +
    • 对js、css、图片等资源文件都支持打包
    • +
    • 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持
    • +
    • 有独立的配置文件webpack.config.js
    • +
    • 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
    • +
    • 支持SourceUrls和SourceMaps,易于调试
    • +
    • 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活
    • +
    • webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快
    • +
    +
  • +
  • webpack、vint、lua
  • +
  • Vue首屏加载慢的优化方案
      +
    • vendor.js过大问题解决
        +
      • 在index.html中使用CDN的资源
      • +
      • 在bulid/webpack.base.conf.js文件中添加externals
      • +
      • 在main.js里将以下import注释替换require引入模块
      • +
      +
    • +
    • vue-cli开启打包压缩和后台配合gzip访问
        +
      • npm install –save-dev compression-webpack-plugin@1.1.11
      • +
      • 打开 config/index.js ,找到 build 对象中的 productionGzip ,改成 true
      • +
      • 此时打包的文件会 新增 .gz 文件
      • +
      • 后台nginx开启gzip模式访问(gzip on;),浏览器访问项目,自动会找到 .gz 的文件
      • +
      +
    • +
    +
  • +
  • 如何用webpack来优化前端性能?(用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。)
      +
    • 压缩代码:删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的uglifyJsPlugin和ParalleluglifyPlugin来压缩JS文件,利用cssnano (css-loader?minimize) 来压缩css,利用 babel-plugin-transform-remove-console删掉console代码
    • +
    • 利用CDN加速:在构建过程中,将引用的静态资源路径修改为CDN.上对应的路径。可以利用webpack对于output参数和各loader的publicPath 参数来修改资源路径
    • +
    • Tree Shaking:将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数–optimize-minimize来实现
    • +
    • Code Splitting:将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存
    • +
    • 提取公共第三方库:SplitChunksPlugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码
    • +
    +
  • +
  • 如何提高webpack的打包速度?
      +
    • happypack:利用进程并行编译loader,利用缓存来使得rebuild更快,遗憾的是作者表示已经不会继续开发此项目,类似的替代者是thread-loader
    • +
    • 外部扩展(externals):将不怎么需要更新的第三方库脱离webpack打包,不被打入bundle中,从而减少打包时间,比如jQuery用script标签引入
    • +
    • dll:采用webpack的DIIPlugin和DIlReferencePlugin引入dll,让-些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
    • +
    • 利用缓存:webpack.cache、babel-loader.cacheDirectory、HappyPack.cache都可以利用缓存提高rebuild效率
    • +
    • 缩小文件搜索范围:比如babel-loader插件,如果你的文件仅存在于src中,那么可以include: path.resolve(_dirname,’sre’),当然绝大多数情况下这种操作的提升有限,除非不小心build了node-modules文件
    • +
    +
  • +
  • 如何提高webpack的构建速度?
      +
    • 多入口情况下,使用CommonsChunkPlugin来提取公共代码
    • +
    • 通过externals配置来提取常用库
    • +
    • 利用D1IPlugin和Dl1ReferencePlugin预编译资源模块通过D11Plugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
    • +
    • 使用Happypack实现多线程加速编译
    • +
    • 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
    • +
    • 使用Tree-shaking和ScopeHoisting来剔除多余代码
    • +
    +
  • +
  • 怎么配置多页应用?
      +
    • 可以使用webpack的AutowebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。要注意的是:
        +
      • 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
      • +
      • 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
      • +
      +
    • +
    +
  • +
+ + + + +

HTTP(S)

    +
  • HTTP2相对于HTTP1.x有什么优势和特点?
      +
    • 二进制分帧:HTTP/2采用二进制格式传输数据,而非HTTP 1.x的文本格式,二进制协议解析起来更高效。
        +
      • 帧: HTTP/2 数据通信的最小单位消息:指HTTP/2中逻辑上的HTTP消息。例如请求和响应等,消息由一个或多个帧组成。
      • +
      • 流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID
      • +
      +
    • +
    • 头部压缩:HTTP2使用“首部表”,只发送差异数据,而不是全部发送,从而减少头部的信息量。
        +
      • HTTP/1.x会在请求和响应中中重复地携带不常改变的、冗长的头部数据,给网络带来额外的负担。
      • +
      • HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送
      • +
      • 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新
      • +
      • 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
      • +
      +
    • +
    +
  • +
  • http的几个状态码,比如:304、200、500、502、504等
      +
    • 2XX成功
        +
      • 200 OK,表示从客户端发来的请求在服务器端被正确处理
      • +
      • 201 Created请求已经被实现,而且有-个新的资源已经依据请求的需要而建立
      • +
      • 202 Accepted请求已接受,但是还没执行,不保证完成请求
      • +
      • 204 No content,表示请求成功,但响应报文不含实体的主体部分
      • +
      • 206 Partial Content, 进行范围请求
      • +
      +
    • +
    • 3XX重定向
        +
      • 301 moved permanently,永久性重定向,表示资源已被分配了新的URL
      • +
      • 302 found, 临时性重定向,表示资源临时被分配了新的URL(302是http1.0的协议状态码,在http1.1版本的时候为 了细化302状态码又出来了两个303和307)
      • +
      • 303 see other, 表示资源存在着另一个URL,应使用GET方法J香获取资源(303明确表示客户端应当采用get方法获取资源,他会把POST请求变为GET请求进行重定向)
      • +
      • 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
      • +
      • 307 temporary redirect,临时重定向,和302含义相同(307会遵照浏览器标准,不会从post变为get)
      • +
      +
    • +
    • 4XX客户端错误
        +
      • 400 bad request,请求报文存在语法错误
      • +
      • 401 unauthorized,表示发送的请求需要有通过HTTP认证的认证信息
      • +
      • 403 forbidden,表示对请求资源的访问被服务器拒绝
      • +
      • 404 not found,表示在服务器.上没有找到请求的资源
      • +
      • 408 Request timeout,客户端请求超时
      • +
      • 409 Confict,请求的资源可能引起冲突
      • +
      +
    • +
    • 5XX服务器错误
        +
      • 500 internal sever error,表示服务器端在执行请求时发生了错误
      • +
      • 501 Not Implemented请求超出服务器能力范围,例如服务器不支持当前请求所需要的某个功能,或者请求是服务
        器不支持的某个方法
      • +
      • 503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
      • +
      • 505 http version not supported服务器不支持,或者拒绝支持在请求中使用的HTTP版本
      • +
      +
    • +
    +
  • +
  • 关于HTTP协议,一篇就够了
  • +
  • 浏览器缓存机制 +
  • +
  • http的状态码200(强缓存)和304(协商缓存)有什么区别
      +
    • 强缓存:直接从本地副本比对读取,不去请求服务器,返回的状态码是200。主要包括expirescache-control
    • +
    • 协商缓存:去服务器比对,若没改变才直接读取本地缓存,返回的状态码是304。主要包括last-modifiedEtag
    • +
    +
  • +
  • 从输入URL到页面展现中间发生了什么
      +
    • 域名解析 -> TCP连接(3次握手)-> 建立连接 -> HTTP请求 -> 后台处理请求 -> HTTP响应 -> 关闭连接 (4次挥手)-> 解析HTML -> 渲染
    • +
    +
  • +
  • 图解tcp三次握手四次挥手
      +
    • 可以四次握手(确认报文段可以拆开发送,可以先发一个确认报文段:ack=x+1,ACK=1,再发送一个同步报文段:SYN=1,seq=y),但不能两次握手
    • +
    • 可以三次挥手(如果服务器收到FIN的时候,也正要关闭连接,就可以将FIN 和ACK一起发送过去)
    • +
    +
  • +
  • TCP传输的三次握手
      +
    • 第一次:SYN=1(请求连接),seq=x(传输数据流序号)
    • +
    • 第二次:SYN=1(接受连接),ack=x+1(ack=seq+1)(确认接受数据流),ACK=1(确认序号),seq=y(传输响应数据序号)
    • +
    • 第三次:ack=y+1,ACK=1(确认序号),seq=x+1(因为第一次是x)。没有SYN,因为SYN这个标志位只有TCP建立连接时才被置为1。
    • +
    +
  • +
  • TCP传输的四次挥手
      +
    • 第一次,FIN=1,seq=i,表示主动断开连接请求
    • +
    • 第二次,ack=i+1,ACK=1,seq=j,表示确认收到信息,同时可能存在服务器没有将数据全部传输完成
    • +
    • 第三次,FIN=1,ack=j+1,ACK=1,seq=k,表示可以(被动)关闭连接,同时可能发送数据
    • +
    • 第四次,ack=k+1,ACK=1,表示确认收到信息,断开连接。
    • +
    +
  • +
  • TCP和UDP的区别
      +
    • TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议
    • +
    • UDP(User Data Protocol,用户数据报协议)是面向非连接的协议
    • +
    +
  • +
  • HTTP和TCP协议是什么关系?
      +
    • 从OSI开放系统互连参考模型(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层)看
    • +
    • TCP属于运输层的协议,主要解决数据如何在网络中传输,负责提供应用进程之间的通信
    • +
    • HTTP属于应用层上的一种协议,主要解决如何包装数据,是上层的协议,需要下层TCP的支持
    • +
    +
  • +
  • HTTPS为什么比较安全(这个关联性不是很大)
      +
    • HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL。包含:证书、卸载、流量转发、负载均衡、页面适配、浏览器适配、refer传递等),这个时候就成了我们常说的HTTPS。
    • +
    +
  • +
  • 创建ajax过程
      +
    • 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
    • +
    • 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
    • +
    • 设置响应HTTP请求状态变化的函数.
    • +
    • 发送HTTP请求.
    • +
    • 获取异步调用返回的数据.
    • +
    • 使用JavaScript和DOM实现局部刷新.
    • +
    +
  • +
  • 如果一条请求返回了跨域提醒,服务器是否已经接收到(是,否则无法返回相应)
  • +
+ + + + +

浏览器

    +
  • 图解浏览器的基本工作原理
      +
    • Browser Process(浏览器主进程):负责包括地址栏,书签栏,前进后退按钮等部分的工作;负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问
    • +
    • Renderer Process:负责一个 tab 内关于网页呈现的所有事情
    • +
    • Plugin Process:负责控制一个网页用到的所有插件,如 flash
    • +
    • GPU Process:负责处理 GPU 相关的任务
    • +
    +
  • +
  • 线程和进程的区别
  • +
  • 浅读V8——强大的JavaScript引擎
      +
    • V8的大致流程:JavaScript源代码 -> 抽象语法树(AST)-> 本地代码
    • +
    • JavaScriptCore:JavaScript源代码 -> 抽象语法树(AST)-> 字节码-> 本地代码
    • +
    +
  • +
  • cookie是干嘛的?有什么用?session又是什么?
  • +
  • 浏览器的缓存机制如何实现的
  • +
  • 怎么查看某个缓存的到期时间及大小
  • +
  • 如何控制开关浏览器缓存
  • +
  • 如何实现跨浏览器保存登录状态
  • +
  • 浏览器自带多少进程
  • +
  • 浏览器自带多少线程
  • +
+ + + + +

WebSocket

    +
  • WebSocket协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。是Web应用程序的传输协议,它提供了双向的,按序到达的数据流。他是一个Html5协议,WebSocket的连接是持久的,他通过在客户端和服务器之间保持双工连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。
  • +
  • websocket(stomp.js+socket.js)
  • +
+ + + + +

安全

    +
  • 常见web安全及防护原理
      +
    • sql注入原理:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
        +
      • 1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双”-“进行转换等。
      • +
      • 2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
      • +
      • 3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
      • +
      • 4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。
      • +
      +
    • +
    • Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。
    • +
    • XSS防范方法:
        +
      • 尽量采用POST而非GET提交表单
      • +
      • 避免直接在cookie中泄露用户隐私,例如email、密码等等
      • +
      • 通过使cookie和系统ip绑定来降低cookie泄露后的危险。这样攻击者得到的cookie没有实际价值,不可能拿来重放
      • +
      • 如果网站不需要再浏览器端对cookie进行操作,可以在Set-Cookie末尾加上HttpOnly来防止javascript 代码直接获取cookie
      • +
      +
    • +
    +
  • +
  • XSS与CSRF有什么区别吗?
      +
    • XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。
    • +
    • CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。(登录受信任网站A,并在本地生成Cookie;在不登出A的情况下,访问危险网站B。)
    • +
    +
  • +
  • CSRF的防御
      +
    • 服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
    • +
    • 通过验证码的方法
    • +
    +
  • +
  • XSS和CSRF攻击原理浅谈
  • +
  • XSS与CSRF攻击的详解与区别
  • +
  • XSS攻击的解决办法:
      +
    • 网站的Cookie设置HttpOnly属性
    • +
    • html escape 转义
    • +
    • 后端永远不要相信前端的数据,服务端的输出检查。
    • +
    +
  • +
  • CSRF攻击的解决办法:
      +
    • Token 验证
    • +
    • Referer Check:根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该 HTTP 请求的来源地址。
    • +
    • 验证码
    • +
    • 尽量不要在页面的链接中暴露用户隐私信息。
    • +
    • 对于用户修改删除等操作最好都使用post 操作 。
    • +
    +
  • +
  • 如何解决xss攻击(慎用v-html、过滤bmp图片、我说了后端请求头加一个属性设置为true即可,具体什么属性我忘了,还有一种是字符串过滤,过滤script标签即可)
  • +
+ + + + +

TypeScript

    +
  • 有哪些操作符?Typeof 是干嘛的?
  • +
  • TS引入一个JS模块?
  • +
  • Declare 关键字是干嘛的?
  • +
  • 什么是类型保护,有什么用?如何触发类型保护?
  • +
  • Never 类型有什么用? + + + +
  • +
+ +

Git

    +
  • Git rebase 和 merge 的区别
  • +
  • docker实现原理及部署命令 + + +
  • +
+ +

实战

    +
  1. 编程:编译一下模板字符串
  2. +
  3. 编程:实现音频和动画的onStart和onEnd异步控制
  4. +
  5. 编程:手动实现new运算符
  6. +
  7. 编程:手动实现instanceof运算符
  8. +
  9. 编程:手写parseInt + + +
  10. +
+ +

前端思想

    +
  • 如何理解前端工程化
  • +
  • webpack做过哪些优化
  • +
  • 如何实现前端性能优化
  • +
  • 你觉得前端工程的价值体现在哪
      +
    • 为简化用户使用提供技术支持(交互部分)
    • +
    • 为多个浏览器兼容性提供支持
    • +
    • 为提高用户浏览速度(浏览器性能)提供支持
    • +
    • 为跨平台或者其他基于webkit或其他渲染引擎的应用提供支持
    • +
    • 为展示数据提供支持(数据接口)
    • +
    +
  • +
  • 谈谈性能优化问题
  • +
  • 渐进增强和优雅降级
      +
    • 渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
    • +
    • 优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
    • +
    +
  • +
  • 前端的可视化库:ECharts、AntV + + +
  • +
+ +

全栈

    +
  • 一个Web应用是怎样运作的,如何优化 + + +
  • +
+ +

小程序

    +
  • 小程序里面存在域的概念吗
  • +
  • 小程序时候踩过哪些坑 + + +
  • +
+ +

Django

+ +

Sql

+ +

数据结构

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性\结构数组链表
元素个数固定按需增减
存储单元定义时分配,要连续程序执行时动态向系统申请,无需连续
元素顺序由位置(下标)确定由指针指向确定
元素增减会频繁移动元素改变指针指向即可
查找效率随机读取效率很高不可随机访问,只能按序
扩展性能空间不够时要重新定义数组链表大小不用定义,数据随意增删
+ + + + +

算法

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
==冒泡排序==O(n^2^)O(n)O(n^2^)O(1)In-Place稳定
选择排序O(n^2^)O(n^2^)O(n^2^)O(1)In-Place不稳定
插入排序O(n^2^)O(n)O(n^2^)O(1)In-Place稳定
希尔排序O(n log n)O(n log^2^ n)O(n log^2^ n)O(1)In-Place不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)Out-Place稳定
==快速排序==O(n log n)O(n log n)O(n^2^)O(log n)In-Place不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)In-Place不稳定
计数排序O(n + k)O(n + k)O(n + k)O(k)Out-Place稳定
桶排序O(n + k)O(n + k)O(n^2^)O(n + k)Out-Place稳定
基数排序O(n * k)O(n * k)O(n * k)O(n + k)Out-Place稳定
+

冒泡排序(Bubble Sort)

    +
  • 时间复杂度:O(n^2^), 最好O(n),最坏O(n^2^)
  • +
  • 空间复杂度:O(1)
  • +
  • 实现思路:
      +
    • 比较相邻的元素。如果第一个比第二个大, 就交换他们两个。
    • +
    • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
    • +
    • 针对所有的元素重复以上的步骤,除了最后一个。
    • +
    • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
function bubbleSort(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = arr.length - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]] // 交换操作
}
}
}
return arr
}
+ +
    +
  • 改进1:设置-标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function bubbleSort2(arr) {
var i = arr.length - 1 // 初始化,最后的交换位置,保持不变
while (i > 0) {
var pos = 0 // 每趟开始时,无记录交换
for (var j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
pos = j // 记录交换的位置
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]] // 交换操作
}
}
i = pos // 为下一趟排序做准备
}
return arr
}
+ +
    +
  • 改进2:传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法,一次可以得到两个最终值(最大者和最小者),从而使排序趟数几乎减少了一半。
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function bubbleSort3(arr) {
var low = 0
var high = arr.length - 1 // 设置变量初始值
var tmp, j
while (low < high) {
for (j = low; j < high; ++j) { // 正向冒泡,找到最大值
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]] // 交换操作
}
}
--high // 前移一位
for (j = high; j >low; --j) { // 反向冒泡,找到最小值
if (arr[j]<arr[j-1]) {
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]] // 交换操作
}
}
++low; // 后移一位
}
return arr
}
+ +

快速排序(Quick Sort)

    +
  • 时间复杂度:O(n log ^n^),最好O(n log ^n^),最坏O(n^2^)
  • +
  • 空间复杂度:O(log ^n^)
  • +
  • 实现思路
      +
    • 基准。从数组中选择中间一项作为基准pivot
    • +
    • 划分。创建两个指针,左边一个指向数组的第一项,右边指向数组最后一项。移动左指针直到我们找到一个比基准大的元素,接着,移动右指针直到找到一个比基准小的元素。然后交换它们,重复这个过程,直到左指针超过了右指针。这个过程是的比基准小的值都排在了基准之前,而比基准大的值都排在了基准之后,这一步叫划分操作
    • +
    • 递归。算法对划分的小数组(较基准小的值组成的子数组,以及较基准大的值组成的子数组)重复之前的两个步骤,直至数组以完全排序。
    • +
    +
  • +
+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const quickSort = (function() { // 立即执行函数
// 默认状态下的比较函数
function compare(a, b) {
if (a === b) {
return 0
}
return a < b ? -1 : 1
}

// 交换操作
function swap(array, a, b) {
[array[a], array[b]] = [array[b], array[a]]
}

// 分治函数
function partition(array, left, right) {
// 用index取中间值,而非splice
const pivot = array[Math.floor((right + left) / 2)]
let i = left
let j = right
while (i <= j) {
while (compare(array[i], pivot) === -1) { // arr[i] < pivot
i++
}
while (compare(array[j], pivot) === 1) { // arr[j] >= pivot
j--
}
if (i <= j) {
swap(array, i, j)
i++
j--
}
}
return i
}

// 快速排序主函数
function quick(array, left, right) {
let index
if (array.length > 1) {
index = partition(array, left, right)
if (left < index - 1) {
quick(array, left, index - 1)
}
if (index < right) {
quick(array, index, right)
}
}
return array
}

// 调用主函数
return function quickSort(array) {
return quick(array, 0, array.length - 1)
}

})()
+ +
    +
  • 阮一峰的思路:
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const quickSort = function(arr) {
if (arr.length <= 1) {
return arr
}
var pivotIndex = Math.floor(arr.length / 2)
var pivot = arr.slice(pivotIndex, 1)[0]
var left = []
var right = []
for (let i = 0; i <= arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([pivot], quickSort(right))
}
+ +
    +
  • 6种二分查找及其变式总结及牢记方法
  • +
  • 实现思路:
      +
    • 折半查找算法要求查找表的数据是线性结构存储,还要求查找表中的顺序是由小到大排序(由大到小排序)
    • +
    • 首先设两个指针,left和right, 表示最低索引和最高索引
    • +
    • 然后取中间位置索引mid,判断mid处的值是否与所要查找的数相同,相同则结束查找,mid处的值比所要查找的值小就把left设为mid+1,如果mid处的值比所要查找的值大就把right设为mid-1
    • +
    • 然后再新区间继续查到,直到找到或者left>right找不到所要查找的值结束查找
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var binarySearch = function (arr, i) {
var left = 0
var right = arr.length - 1
while (left <= right) {
var mid = Math.floor((left + right) / 2)
if (i < arr[mid]) {
right = mid - 1
} else if (i > arr[mid]) {
left = mid + 1
} else if (i === arr[mid]) {
return mid
}
}
return false
}
+ +

深度优先遍历(DFS)

    +
  • 深度优先遍历(DFS)和广度优先遍历(BFS)
  • +
  • 实现思路:
      +
    • 从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个顶点进行访问
    • +
    • 重复此步骤,直至所有结点都被访问完为止
    • +
    +
  • +
+
DFS 递归写法
1
2
3
4
5
6
7
8
9
10
11
12
function deepTraversal(node, nodeList) {
if (node) {
nodeList.push(node)
var children = node.children
for (var i = 0; i < children.length; i++) {
deepTraversal(children[i], nodeList)
}
}
return nodeList
}
var root = document.getElementById('root')
console.log(deepTraversal(root,nodeList=[]))
+ +
DFS 非递归写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function deepTraversal(node) {
var nodeList = []
if (node) {
var stack = []
stack.push(node)
while (stack.length != 0) {
var childrenItem = stack.pop()
nodeList.push(childrenItem)
var childrenList = childrenItem.children
for (var i = childrenList.length - 1; i >= 0; i--) {
stack.push(childrenList[i])
}
}
}
return nodeList
}
var root = document.getElementById('root')
console.log(deepTraversal(root))
+ +

广度优先遍历(BFS)

    +
  • 深度优先遍历(DFS)和广度优先遍历(BFS)
  • +
  • 实现思路:
      +
    • 从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点所有未被访问的邻结点,访问完后再访问这些结点中第一个邻结点的所有结点
    • +
    • 重复此方法,直到所有结点都被访问完为止
    • +
    +
  • +
+
BFS 非递归写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function wideTraversal(node) {
var nodes = []
if (node != null) {
var queue = []
queue.unshift(node)
while (queue.length != 0) {
var item = queue.shift()
nodes.push(item)
var children = item.children
for (var i = 0; i < children.length; i++) {
queue.push(children[i])
}
}
}
return nodes
}
var root = document.getElementById('root')
console.log(wideTraversal(root))
+ + + + +

未分类试题

    +
  • 前端和后端的区别
  • +
  • http和http2.0
  • +
  • http和https
  • +
  • 如果要把http升级成https应该怎么做
  • +
  • get和post的区别
  • +
  • 浏览器缓存机制
  • +
  • 浏览器缓存存放位置
  • +
  • 浏览器从输入url到页面生成结果哪些步骤
  • +
  • tcp三次连接
  • +
  • 进程和线程
  • +
  • bfc是什么,解决哪些问题
  • +
  • ajax的原理和缺点
  • +
  • typeof和instanceof的区别
  • +
  • 有几种方式判断Array类型
  • +
  • 函数深拷贝和浅拷贝的区别,怎么实现深拷贝
  • +
  • js垃圾回收机制
  • +
  • 事件循环
  • +
  • 事件冒泡
  • +
  • 阻止事件冒泡的方式,ie是什么方式
  • +
  • this的情况
  • +
  • js的继承方式,有什么缺点
  • +
  • new做了什么事情
  • +
  • node线程池
  • +
  • node有哪些模块
  • +
  • node的事件循环和浏览器的有什么区别
  • +
  • node应该怎么读取2DB的数据
  • +
  • vue的双向绑定是怎么做到的
  • +
  • vue的diff算法是怎么样的
  • +
  • vue cil做了哪些事情,你会怎么设计
  • +
  • MVC,ajax, 布局,JQuery
  • +
  • “敏感信息”(密码、身份证之类的)的处理
  • +
  • 页面优化相关
  • +
  • px rem em
  • +
  • border-radius,完整写法几个值
  • +
  • 实现一个背景色,一半红一半白
  • +
  • 什么场景使用HTTP,什么场景使用HTTPS
  • +
  • web前端学习到了什么程度
  • +
  • MVVM和MVC的区别
  • +
  • Vue哪些对数组的操作不会导致页面变化
  • +
  • websocket原理
  • +
  • nocache和nostore的区别
  • +
  • 有没有做哪些自适应的页面布局(栅格,圣杯布局)
  • +
  • cookie和localstorage的区别
  • +
  • cookie可以取哪些值
  • +
  • xss是什么如何防范
  • +
  • csp了解吗
  • +
  • async await settimeout promise一堆放在一起的执行结果
  • +
  • 隐式类型转换
  • +
  • 写个css布局左右定宽中间自适应
  • +
  • 数组去掉(false,null, undefine)去重后从大到小排序
  • +
  • 给一个对象数组,是一个树的形式存储的城市代码,要求遍历找到id为x的节点输出城市名
  • +
  • css怎么实现水平垂直居中
  • +
  • css如何处理类名冲突的问题
  • +
  • plugin的原理
  • +
  • promise怎么实现
  • +
  • ip协议有哪些
  • +
  • f5随机生成0-5的随机数,如何构造f7
  • +
  • 一个桥有20个格子,出发点有无限多的米,一个人每前进一步需要吃十粒米,后退不需要,问如何能够走到桥对面
  • +
+ + + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/08/12/2020%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/10/27/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225/index.html" "b/2020/10/27/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225/index.html" new file mode 100644 index 000000000..896ed419c --- /dev/null +++ "b/2020/10/27/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225/index.html" @@ -0,0 +1,309 @@ +qiankun 2.0.24 爬坑记录 | 淳淳同学的个人博客 + + + + + + + + + + + + +

qiankun 2.0.24 爬坑记录

+

由于本次开发项目需要嵌入之前的老项目,由于考虑到iframe速度慢、css/js需要额外请求、阻塞页面加载、浏览器前进/后退等缺点,遂打算踩坑qiankun,为了更早的爬坑,整理此文。

+
+

简介

qiankun 是一个基于 single-spa微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

+

官方提供的资源:

+ +

根据 qiankun官方文档 介绍,主要有以下七大特性:

+
    +
  • 📦 基于 single-spa 封装,提供了更加开箱即用的 API。
  • +
  • 📱 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
  • +
  • 💪 HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
  • +
  • 🛡​ 样式隔离,确保微应用之间样式互相不干扰。
  • +
  • 🧳 JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
  • +
  • ⚡️ 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
  • +
  • 🔌 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。
  • +
+

行业内其他前端团队对微前端的看法和实践:

+ +

API介绍

此处只介绍api的简单功能描述,如想继续了解请移步官方文档

+

registerMicroApps(apps, lifeCycles?)

注册微应用的基础配置信息。当浏览器 url 发生变化时,会自动检查每一个微应用注册的 activeRule 规则,符合规则的应用将会被自动激活。

+
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
33
34
35
36
37
38
39
40
41
42
import { registerMicroApps } from 'qiankun';

registerMicroApps(
[
{
// name - string - 必选,微应用的名称,微应用之间必须确保唯一
name: 'apass-micro',
// entry - string - 必选,微应用的入口
entry: 'localhost:8080',
// container - string | HTMLElement - 必选,微应用的容器节点的选择器或者 Element 实例
container: '#apassMicroTemplateConfig',
// activeRule - string - 必选,微应用的激活规则
activeRule: '/index/config/template/edit',
// props - object - 可选,主应用需要传递给微应用的数据
props: {
name: 'kuitos',
routerPushFunc: (that) => {
that.$router.push('/713/5f4f65fabcb7c173/fields')
},
data: {
// 已响应式的数据通信
store: microAppStore.getGlobalState
},
}
}
],
{
beforeLoad: app => console.log('before load', app.name),
beforeMount: [
app => console.log('before mount', app.name),
],
afterMount: [
app => console.log('after mount', app.name),
],
beforeUnmoun: [
app => console.log('before unmount', app.name),
],
afterUnmount: [
app => console.log('after unmount', app.name),
]
},
);
+ +

start(opts?)

启动 qiankun

+
1
2
3
import { start } from 'qiankun';

start();
+ +

设置主应用启动后默认进入的微应用。

+
1
2
3
import { setDefaultMountApp } from 'qiankun';

setDefaultMountApp('/homeApp');
+ +

runAfterFirstMounted(effect)

第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。

+
1
2
3
4
5
6
import { runAfterFirstMounted } from 'qiankun';

runAfterFirstMounted(() => {
console.log('第一个子应用加载完后,该方法被调用')
this.otherFunction()
})
+ +

loadMicroApp(app, configuration?)

适用于需要手动 加载/卸载 一个微应用的场景。

+

通常这种场景下微应用是一个不带路由的可独立运行的业务组件。 微应用不宜拆分过细,建议按照业务域来做拆分。业务关联紧密的功能单元应该做成一个微应用,反之关联不紧密的可以考虑拆分成多个微应用。 一个判断业务关联是否紧密的标准:看这个微应用与其他微应用是否有频繁的通信需求。如果有可能说明这两个微应用本身就是服务于同一个业务场景,合并成一个微应用可能会更合适。

+
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
import { loadMicroApp } from 'qiankun';

// 因为loadMicroApp()返回子应用的实例,拿一个全局变量接收后续可进行其他操作如:手动卸载子应用
this.microApp = loadMicroApp(
{
name: 'sub-vue',
entry: 'http://localhost:7777/subapp/sub-vue',
container: '#apassMicroTemplateConfig',
props: {
routerBase: '/index/config/template/edit',
getGlobalState: microAppStore.getGlobalState,
sheetId: '2133123123'
}
},
{
// sandbox - boolean | { strictStyleIsolation?: boolean, experimentalStyleIsolation?: boolean } - 可选,是否开启沙箱,默认为 true
sandbox: { strictStyleIsolation: true },
// singular - boolean | ((app: RegistrableApp<any>) => Promise<boolean>); - 可选,是否为单实例场景,单实例指的是同一时间只会渲染一个微应用。默认为 false
singular: true
}
)

// 封装卸载子应用的函数
private unmountMicroApp () {
if (this.microApp) {
this.microApp.mountPromise.then(() => {
this.microApp.unmount()
})
}
}
+ +

prefetchApps(apps, importEntryOpts?)

手动预加载指定的微应用静态资源。仅手动加载微应用场景需要,基于路由自动激活场景直接配置 prefetch 属性即可。

+
1
2
3
import { prefetchApps } from 'qiankun';

prefetchApps([ { name: 'app1', entry: '//locahost:7001' }, { name: 'app2', entry: '//locahost:7002' } ])
+ + +

主应用配置

安装qiankun

1
$ npm i qiankun -S # 或者 yarn add qiankun
+ +

调整main.js

如果你需要在项目初始化的时候就加载这些子应用,那么需要修改main.js的一些配置;如果是在页面中手动加载可略过此步。

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import Vue from "vue"
import App from "./App.vue"
import router from "./router"

import { registerMicroApps, setDefaultMountApp, start } from "qiankun"
Vue.config.productionTip = false
let app = null;
/**
* 渲染函数
* appContent 子应用html内容
* loading 子应用加载效果,可选
*/
function render({ appContent, loading } = {}) {
if (!app) {
app = new Vue({
el: "#container",
router,
data() {
return {
content: appContent,
loading
};
},
render(h) {
return h(App, {
props: {
content: this.content,
loading: this.loading
}
});
}
});
} else {
app.content = appContent;
app.loading = loading;
}
}

/**
* 路由监听
* @param {*} routerPrefix 前缀
*/
function genActiveRule(routerPrefix) {
return location => location.pathname.startsWith(routerPrefix);
}

function initApp() {
render({ appContent: '', loading: true });
}

initApp();

// 传入子应用的数据
let msg = {
data: {
auth: false
},
fns: [
{
name: "_LOGIN",
_LOGIN(data) {
console.log(`父应用返回信息${data}`);
}
}
]
};

// 注册子应用
registerMicroApps(
[
{
name: "sub-app-1",
entry: "//localhost:8091",
render,
activeRule: genActiveRule("/app1"),
props: msg
},
{
name: "sub-app-2",
entry: "//localhost:8092",
render,
activeRule: genActiveRule("/app2"),
}
],
{
beforeLoad: [
app => {
console.log("before load", app);
}
], // 挂载前回调
beforeMount: [
app => {
console.log("before mount", app);
}
], // 挂载后回调
afterUnmount: [
app => {
console.log("after unload", app);
}
] // 卸载后回调
}
);

// 设置默认子应用,与 genActiveRule中的参数保持一致
setDefaultMountApp("/app1");

// 启动
start();
+ +

修改App.vue中的id 或 增加渲染子应用的盒子

因为一个主应用可能会嵌套多个子应用,所以App.vue难免会重名,所以最好加一个自己项目名称的前缀来做区分。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div id="main-root">
<!-- loading -->
<div v-if="loading">loading</div>
<!-- 子应用盒子 -->
<div id="root-view" class="app-view-box" v-html="content"></div>
</div>
</template>

<script>
export default {
name: "App",
props: {
loading: Boolean,
content: String
}
};
</script>
+ + + +

配置vue子应用

因为子应用本身就是一个单独的应用,所以不必安装qiankun,只需要暴露被当做子应用嵌入时,qiankun所需的3个生命周期即可。

+

配置maim.js

在支持被当做子应用嵌入的同时,需要支持项目独立运行,兼容之前配置

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
import './public-path';

Vue.config.productionTip = false;

let router = null;
let instance = null;

function render() {
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/app1' : '/',
mode: 'history',
routes,
});

instance = new Vue({
router,
render: h => h(App),
beforeMount () {
if (window.__POWERED_BY_QIANKUN__) {
routerPushFunc(this)
AppModule.SET_CURRENT_ENV()
}
}
}).$mount(container ? container.querySelector('#templateConfig') : '#templateConfig');
}

if (!window.__POWERED_BY_QIANKUN__) {
render();
}

export async function bootstrap() {
console.log('vue app bootstraped');
}

export async function mount(props) {
console.log('props from main app', props);
render();
}

export async function unmount() {
(instance as Vue).$destroy();
(instance as Vue).$el.innerHTML = ''; // 防止内存泄漏,子项目销毁时清空dom
instance = null;
router = null;
}
+ +

public-path.js

使用 webpack 静态 publicPath 配置:可以通过两种方式设置,一种是直接在 mian.js 中引入 public-path.js 文件,一种是在开发环境直接修改 vue.config.js

+
1
2
3
4
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
+ +

配置 vue.config.js

子应用必须支持跨域:由于 qiankun 是通过 fetch 去获取子应用的引入的静态资源的,所以必须要求这些静态资源支持跨域

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const path = require('path');
const { name } = require('./package');

function resolve(dir) {
return path.join(__dirname, dir);
}

const pagesMicro = {
templateConfig: {
entry: 'src/microPage/templateConfig/main.ts',
template: 'src/microPage/templateConfig/index.html',
chunks: ['runtime~templateConfig', 'chunk-vendors', 'chunk-common', 'templateConfig']
},
}

const pagesMain = {
index: {
entry: 'src/main.ts',
template: '/index.html'
}
}

const pages = process.env.VUE_APP_ENTRY === 'main' ? pagesMain : pagesMicro

let config = {
/**
* You will need to set publicPath if you plan to deploy your site under a sub path,
* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then publicPath should be set to "/bar/".
* In most cases please use '/' !!!
* Detail: https://cli.vuejs.org/config/#publicpath
*/
outputDir: 'dist',
assetsDir: 'static',
filenameHashing: true,
// tweak internal webpack configuration.
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
devServer: {
// host: '0.0.0.0',
hot: true,
disableHostCheck: true,
port,
overlay: {
warnings: false,
errors: true,
},
headers: {
'Access-Control-Allow-Origin': '*',
},
},
// 自定义webpack配置
configureWebpack: {
resolve: {
alias: {
'@': resolve('src'),
},
},
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
},
};

if (process.env.VUE_APP_ENTRY === 'micro') {
config.pages = pagesMicro
}

module.exports = config
+ + +

qiankun常见问题及解决方案

避免 css 污染

qiankun 只能解决子项目之间的样式相互污染,不能解决子项目的样式污染主项目的样式,技术与规范方面大约有这 5 种方案:

+
    +
  • vue自带的scope
      +
    • 只能解决一部分页面内的样式污染,但一般不会有这个问题
    • +
    +
  • +
  • BEM命名方式
  • +
  • css-in-js
      +
    • 学习曲线高;可读性差;借助前端堆栈消耗性能;
    • +
    +
  • +
  • css-loader
      +
    • 开启css-modules,类似于图片懒加载,替换attr
    • +
    • 缺点:页面中需要把class写成css-modules的形式;样式多了之后都是hash的形式可读性不高;
    • +
    +
  • +
  • postcss-loader
      +
    • 利用postcss-modules插件的getJson()函数将所有css文件中的class转为json对象;利用postcss-html把json对象渲染回html页面的class
    • +
    • 缺点:利用新的gulp,意义不大;每次修改都要编译,很慢;
    • +
    +
  • +
+

拿css-loader举例,开启css-modules,可参考以下文章:

+ +
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
module.exports = {
// ... 省略其他配置
css: {
// 是否使用css分离插件 ExtractTextPlugin
extract: false,
// 开启 CSS source maps?
sourceMap: false,
// css预设器配置项
loaderOptions: {
css: {
// These properties are valid:
// object { url?, import?, modules?, sourceMap?, importLoaders?, localsConvention?, onlyLocals?, esModule? }
modules: {
// These properties are valid:
// object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
exportGlobals: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
},
localsConvention: 'asIs' // asIs camelCase camelCaseOnly dashes dashesOnly
}
},
// 启用 CSS modules for all css / pre-processor files.
requireModuleExtension: true
},
}
+ +

谨慎使用 position:fixed

在子项目中这个定位会出现问题,基本出现在模态框和抽屉的定位上,应尽量避免使用,确有相对于浏览器窗口定位需求,可以用 position: sticky,但是会有兼容性问题(IE不支持)。如果定位使用的是 bottom 和 right,则问题不大。
还有个办法,位置可以写成动态绑定 style 的形式:

+
1
<div :style="{ top: isQiankun ? '10px' : '0'}">
+ +

给 body 、 document 等绑定的事件,请在 unmount 周期清除

js 沙箱只劫持了 window.addEventListener,使用 document.body.addEventListener 或者 document.body.onClick 添加的事件并不会被沙箱移除,会对其他的页面产生影响,请在 unmount 周期清除

+

报错:Uncaught Error application ‘xxx’ died in status LOADING_SOURCE_CODE: [qiankun] You need to export lifecycle functions in xxx entry

一般就是打包姿势不对,可能原因:未打包成umd格式;所需的js文件虽然被整体打包了但没被加载,需要利用runtimeChunk单独打包出来

+

现刷新页面报错,容器找不到

解决方案1:在组件 mounted 周期注册并启动 qiankun

+

解决方案2:new Vue() 之后,等 DOM 加载好了再注册并启动 qiankun

+
1
2
3
4
5
6
7
8
const vueApp = new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
vueApp.$nextTick(() => {
//在这里注册并启动 qiankun
})
+ +

主、子应用的路由,均可用 history 模式

因为vue-router的history模式是全匹配的,所以如果当前子应用是被qiankun嵌入时,需要在子应用的一级路由前加上主应用除了http://ip+port/后的所有路由,即在主应用中初始子应用是定义的activeRule

+
1
2
3
4
5
6
7
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/templateConfig' : '/',
mode: 'history',
routes: [
{ ... }
]
})
+ +

history模式下,主、子应用的路由配置问题

如果主、子应用的vue-router都是history模式(即路由全匹配)时

+
    +
  • 主应用中的route信息的path属性需要改为’index/edit*’的形式,即模糊全匹配,而且子应用的跟路由需要改为’index/edit/‘的形式(上面说过了)。否则子应用改变路由后,主应用匹配不到当前页面,则会跳回登录页会调至404。
  • +
  • 子应用中的route信息里最好不要有’’或者’*’之类的判空。否则主应用(从嵌入子应用的那个页面)跳转到其他页面后,会触发子应用的路由匹配规则,进而跳转至子应用的登录页,而且导致主应用的路由跳转失败(也不能叫失败,实际上是跳转出去了又被redirect重定向回来了)。
  • +
+

从一个子项目跳转到另一个子项目

在子项目里面如何跳转到另一个子项目/主项目页面呢,直接写 或者用 router.push/router.replace 是不行的,原因是这个 router 是子项目的路由,所有的跳转都会基于子项目的 base 。写 链接可以跳转过去,但是会刷新页面,用户体验不好。

+

解决办法也比较简单,在子项目注册时将主项目的路由实例对象传过去,子项目挂载到全局,用父项目的这个 router 跳转就可以了。

+

但是有一丢丢不完美,这样只能通过 js 来跳转,跳转的链接无法使用浏览器自带的右键菜单

+

图片资源报错404

最好改为绝对路径

+
1
2
3
<img src="./img/logo.jpg">
<!-- 改为 -->
<img src="/img/logo.jpg">
+ +

或者在主应用中配置nginx静态文件的代理(这里没有后台的nginx配置,所以拿webpack自带的proxyTable代理作示例)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (item === '/index/config/template/edit/static') { // 登录页img
proxyObj[item] = {
target: 'http://localhost:8081',
ws: false,
changeOrigin: true,
pathRewrite: { '^/index/config/template/edit/static': '/static' }
}
} else if (item === '/static/home') { // 首页img
proxyObj[item] = {
target: 'http://localhost:8081',
ws: false,
changeOrigin: true,
pathRewrite: { '^/static/home': '/static/home' }
}
}
+ +

手动加载子应用时,如果子应用的js文件太大会造成阻塞

如果是手动加载子应用,即loadMicroApp(),推荐在页面初始化的时候就预加载资源,即prefetchApps()。避免请求的pending时间太长阻塞加载

+

ts项目与js项目文件加载的问题

因为主项目是ts,默认加载的是ts文件;但子项目是js。所以在子项目中引入js文件的时候要标清楚后缀名,例如

+
1
2
3
4
5
// 会报错  Unknown custom element: <widget> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
import {widgetInRecord as widget} from '@/views/sheetConfig/fieldConfig/widget/widget'

// 加上后缀名就不报错了
import {widgetInRecord as widget} from '@/views/sheetConfig/fieldConfig/widget/widget.js'
+ + +

在一个页面内以不同的初始化数据加载同一子应用(如:左侧是列表,右侧的详情是qiankun嵌入的子应用)

重复加载问题、数据通信问题、请求响应问题

+
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
```

## 主项目与子项目的数据通信

项目之间的不要有太多的数据依赖,毕竟项目还是要独立运行的。通信操作需要判断是否 qiankun 模式,做兼容处理。

通过 props 传递父项目的 Vuex ,如果子项目是 vue 技术栈,则会很好用。假如子项目是 jQuery/react/angular ,就不能很好的监听到数据的变化。

qiakun 提供了一个全局的 GlobalState 来共享数据。主项目初始化之后,子项目可以监听到这个数据的变化,也能提交这个数据。

```js
// 主项目初始化
import { initGlobalState } from 'qiankun';

const actions = initGlobalState(state);

// 主项目项目监听和修改
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});

actions.setGlobalState(state);

// 子项目监听和修改
export function mount(props) {
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});
props.setGlobalState(state);
}
+ +

vue子项目内存泄露问题

这个问题挺难发现的,是在 qiankun 的 issue 区看到的: github.com/umijs/qiank… ,排查过程我就不发了,解决方案挺简单。

+

子项目销毁时清空 dom 即可:

+
1
2
3
4
5
6
export async function unmount() {
instance.$destroy();
+ instance.$el.innerHTML = ""; //新增这一行代码
instance = null;
router = null;
}
+ +

但是其实,来回切换子项目并不会使内存不断增加。也就是说,即使卸载子项目时,子项目占用的内存没有被释放,但是下次加载时会复用这块内存,那这样的话,子项目会不会加载更快?(还未考证)

+

安全和性能的问题

qiankun 将每个子项目的 js/css 文件内容都记录在一个全局变量中,如果子项目过多,或者文件体积很大,可能会导致内存占用过多,导致页面卡顿。

+

另外,qiankun 运行子项目的 js,并不是通过 script 标签插入的,而是通过 eval 函数实现的,eval 函数的安全和性能是有一些争议的:MDN的eval介绍

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/10/27/qiankun-2-0-24-%E7%88%AC%E5%9D%91%E8%AE%B0%E5%BD%95/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Announcement
This is my Blog
Recent Posts
\ No newline at end of file diff --git "a/2020/11/07/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts/index.html" "b/2020/11/07/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts/index.html" new file mode 100644 index 000000000..cb01eb6dd --- /dev/null +++ "b/2020/11/07/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts/index.html" @@ -0,0 +1,183 @@ +中央事件总线插件vue-bus-ts | 淳淳同学的个人博客 + + + + + + + + + + + + +

中央事件总线插件vue-bus-ts

+

项目中难免会遇到非父子组件之间的传参与通信问题,遂整理此文。

+
+

简介

vue-bus-ts 是一款支持在ts环境下使用的全局事件总线插件。

+

安装及使用方法

安装

1
npm i -S vue-bus-ts
+ +

注册

安装后需要在main.ts中引入并注册,挂载到全局的Vue实例上即可

+
1
2
3
4
5
6
7
8
9
10
11
12
# main.ts

import Vue from 'vue';
import EventBus from 'vue-bus-ts';

Vue.use(EventBus);
var bus = new EventBus.Bus();

new Vue({
bus,
render: (h) => h(App),
}).$mount('#app');
+ +

注册事件

在调用事件前,需要实现注册该事件,否则不会生效

+

一般写在vue文件的mounted或者created生命周期中

+
1
2
3
4
5
6
# *.vue

var eventId = this.$bus.$on('event_name', function (params: any) {
// params is the parameter passed in by $emit
// do something...
})
+ +

调用事件

注册成功后,打印this.$bus即可看到当前的事件总线实例,下面为调用的方法

+
1
2
3
4
5
6
7
8
9
10
11
12
# *.vue
// params can pass in any form of value, including Array, Object, String, Number, null, undefined or even array expansion items.or example,

var eventResult = this.$bus.$emit('event_name', params)

1. var result = this.$bus.$emit('event_name', [1,2,3])
2. var result = this.$bus.$emit('event_name', {})
3. var result = this.$bus.$emit('event_name', 'string')
4. let a = 1, b = 'test', c = [1, 2, 3], d = {a: 'test'}
var result = this.$bus.$emit('event_name', a, b ,c, d)
5. var result = this.$bus.$emit('event_name', null)
6. var result = this.$bus.$emit('event_name')
+ +

注销事件

如果不注销的话,下一次$on注册同一个事件时,会生成两个相同的事件,调用时会触发n次。所以建议在离开当前页面或当前模块时注销该事件,即写在beforeRouterLeave中(如果开启了keep-alive,可写在deactivated中)。

+
1
2
3
# *.vue

this.$bus.$off('event_name', eventId) // To unbind event binding, eventId is the return value of this.$bus.$on
+ +

订阅事件?

1
2
3
4
5
6
# *.vue

let result = this.$bus.$subscribed('event_name')
if (result) {
// do something...
}
+ +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/11/07/%E4%B8%AD%E5%A4%AE%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BA%BF%E6%8F%92%E4%BB%B6vue-bus-ts/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git a/2020/11/10/jsdelivr/index.html b/2020/11/10/jsdelivr/index.html new file mode 100644 index 000000000..d3d8642da --- /dev/null +++ b/2020/11/10/jsdelivr/index.html @@ -0,0 +1,193 @@ +jsdelivr | 淳淳同学的个人博客 + + + + + + + + + + + + +

jsdelivr

+

本文概要

+
+

CDN

jsdelivr for GitHub

需要配合PicGo图床使用

+
    +
  • 使用PicGo客户端上传图片
  • +
  • 复制url
      +
    • 默认格式为:PicGo默认前缀 + GitHub用户名 + / + 仓库名 + / + 分支名 + 图片在GitHub仓库的绝对路径
    • +
    • 例如:https://raw.githubusercontent.com/LeeDebug/PicGo/master/img/xxx.png
    • +
    +
  • +
  • 替换url中的前缀部分
      +
    • 默认格式为jsdeliver默认前缀 + 图片在GitHub仓库的绝对路径
    • +
    • 例如:https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/xxx.png
    • +
    +
  • +
+

测试图片:
GitHub自带的CND

+

图床

PicGo+GitHub

支持桌面客户端上传、快捷键上传;仅支持普通url复制;

+

参考文档:

+ +

测试图片:
PicGo+GitHub

+

新浪图床

chrome插件上传;支持多种格式url复制;

+

测试图片:
新浪图床

+

搜狗图床

网页上传;不支持url复制;

+

测试图片:
搜狗图床

+

奇虎图床

网页上传;不支持url复制;

+

测试图片:
奇虎图床

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/11/10/jsdelivr/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/11/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key/index.html" "b/2020/12/11/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key/index.html" new file mode 100644 index 000000000..782d82e24 --- /dev/null +++ "b/2020/12/11/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key/index.html" @@ -0,0 +1,201 @@ +Mac配置多个SSH-Key | 淳淳同学的个人博客 + + + + + + + + + + + + +

Mac配置多个SSH-Key

+

随着项目与能力的提升与扩展,一台电脑上同时用着多个git仓库的情况越来越普遍,所以我们需要创建多个ssh key来对应不同的账号。本文以github为例

+
+

本地配置ssh秘钥和公钥

进入到ssh文件夹下

1
cd ~/.ssh/
+ +

生成一个ssh-key

引号内填写你github对应的邮箱

+
1
→ ssh-keygen -t rsa -b 4096 -C "your email"
+ +

如果你之前mac上创建过ssh-key,在.ssh文件夹下会有id_rsaid_rsa.pub两个文件,分别为秘钥和公钥;当再次创建时,会有如下提示:

+
1
2
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/xxx/.ssh/id_rsa):
+ +

如果想覆盖之前的文件直接回车即可;如果想创建新的ssh-key,则需要在此处输入新的名称,如id_rsa_github,回车后会提示你下次使用此ssh-key时是否需要密码,如果是个人电脑,直接回车即可(如果需要,设置便可)

+
1
2
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
+ +

设置完密码后,会提示你ssh-key设置成功

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
our identification has been saved in id_rsa_github.
Your public key has been saved in id_rsa_github.pub.
The key fingerprint is:
SHA256: hash值 你的邮箱
The key's randomart image is:
+---[RSA 4096]----+
| .@/0+ o |
| o=+0*O o |
| .o=.*** .|
| o.o B = |
| @ + + * .|
| o + + o . |
| o . o |
| |
| |
+----[SHA256]-----+
+ +

查看.ssh文件夹下的新文件,则会看到刚才新生成的id_rsa_github秘钥和id_rsa_github.pub公钥

+
1
2
3
4
5
ls
id_rsa
id_rsa.pub
id_rsa_github
id_rsa_github.pub
+ +

将ssh-key添加到ssh-agent

因为本地默认只读id_rsa,我们想要使用新的秘钥对则需要把新添加的ssh-key添加到ssh-agent

+

首先,查看ssh agent所有密钥对(如果有以下提示,则表示从未添加过)

+
1
2
→ ssh-add -l
The agent has no identities.
+ +

将新的ssh-key添加到ssh agent

+
1
2
→ ssh-add id_rsa_github
Identity added: id_rsa_github (id_rsa_github)
+ +

此时再次查看ssh agent

+
1
2
→ ssh-add -l
4096 SHA256: hash值 id_rsa_github (RSA)
+ +

配置github的ssh-key

首先查看我们刚配置好的ssh-key的公钥,即以*.pub为后缀名的文件。复制整个文件的内容,即公钥信息

+
1
2
cat id_rsa_github.pub
ssh-rsa 公钥 你的邮箱
+ +

登录github后,在右上角的头像下拉列表中选择Settings选项,在左侧菜单中选择SSH and GPG keys,即可看到当前的SSH Keys,点击右上角绿色的New SSH Key按钮,在Key下方的文本框中,粘贴你的公钥信息,会默认以你的邮箱作为title(也可自行更改),点击下方的Add SSH Key即可。步骤如下图所示

+

github的ssh-key

+

测试连接

回到自己的项目仓库下git fetch;或打开控制台,输入以下命令

+
1
2
→ ssh -T git@github.com
Hi LeeDebug! You've successfully authenticated, but GitHub does not provide shell access.
+ +

到此,你的ssh-key已经可以正常使用,你可以使用ssh的方式去clone项目了

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/11/Mac%E9%85%8D%E7%BD%AE%E5%A4%9A%E4%B8%AASSH-Key/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/11/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225/index.html" "b/2020/12/11/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225/index.html" new file mode 100644 index 000000000..6d4008fcf --- /dev/null +++ "b/2020/12/11/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225/index.html" @@ -0,0 +1,179 @@ +git推送后没有贡献记录 | 淳淳同学的个人博客 + + + + + + + + + + + + +

git推送后没有贡献记录

+

github的contribution记录墙不展示问题

+
+

问题背景

最近打算把自己的项目同时部署到github、gitlab、gitee上,并且都改成了统一的email邮箱和username用户名,但是发现只有github没有任何的contribution记录。

+

于是我把本地的git信息及mac钥匙串保存的信息都删掉,重新记录了一遍;把SSH KEY也都删掉重新加了一遍。但都么有解决问题。

+

最后发现是github和其他两个仓库用的邮箱不一样,本地git的全局配置邮箱为a,gitee和gitlab用的邮箱也是a,只有github用的邮箱为b;所以github可能认为提交者不是当前用户,所以就没有提交记录。

+

解决方案

所以将多个仓库的邮箱改为同一个,并且将全局的gitconfig的email也改成此邮箱,即可。

+

命令代码

查看全局git信息:

+
1
2
3
4
5
git config --global --list

# 或

cat ~/.gitconfig
+ +

修改全局的git信息的邮箱:

+
1
git config --gloabl user.email "your github email"
+ +

配置本仓库的git信息的邮箱:

+
1
git config user.email "your github email"
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/11/git%E6%8E%A8%E9%80%81%E5%90%8E%E6%B2%A1%E6%9C%89%E8%B4%A1%E7%8C%AE%E8%AE%B0%E5%BD%95/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/11/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272/index.html" "b/2020/12/11/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272/index.html" new file mode 100644 index 000000000..d82fa64e7 --- /dev/null +++ "b/2020/12/11/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272/index.html" @@ -0,0 +1,170 @@ +node打包内存溢出 | 淳淳同学的个人博客 + + + + + + + + + + + + +

node打包内存溢出

+

vue项目利用webpack打包时提示内存堆栈溢出

+
+

报错信息

1
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
+ +

解决方案

在build前,根据自己的项目大小设置一下最大分配内存空间,即可。

+
1
2
3
4
5
// 在package.json中
"scripts": {
...
"build": "node --max_old_space_size=4096 build/build.js"
}
+ +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/11/node%E6%89%93%E5%8C%85%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/14/git\350\264\241\347\214\256\345\242\231gitcalendar/index.html" "b/2020/12/14/git\350\264\241\347\214\256\345\242\231gitcalendar/index.html" new file mode 100644 index 000000000..74eeb6fe8 --- /dev/null +++ "b/2020/12/14/git\350\264\241\347\214\256\345\242\231gitcalendar/index.html" @@ -0,0 +1,260 @@ +git贡献墙 gitcalendar | 淳淳同学的个人博客 + + + + + + + + + + + + +

git贡献墙 gitcalendar

+

公司为了访问便捷,大都使用gitlab或gitee,所以前两年的贡献度都在这两个仓库上。从今年打算转战github,多做项目、多做整理、多输出文章,旨在提升自己。所以本文的git贡献墙也以github为例

+
+

诸多解决方案

Github Official API

参考地址:

+ +

优势:

+
    +
  • 官方的
  • +
+

缺点:

+
    +
  • 速度慢
  • +
  • 不稳定,可能会挂
  • +
  • 单纯的canvas
  • +
  • 没有可读数据
  • +
+

Github Chart API

参考地址:

+ +

优势:

+
    +
  • 实时性好,与官网同步
  • +
+

缺点:

+
    +
  • 速度慢
  • +
  • 不稳定,可能会挂
  • +
  • 单纯的canvas
  • +
  • 没有可读数据
  • +
+

github-calendar.js

参考地址:

+ +

优势:

+
    +
  • 自带色卡
  • +
+

缺点:

+
    +
  • 作者不维护了
  • +
+

GitHub Contribution Calendar API

参考地址:

+ +

优势:

+
    +
  • 有数据返回,可读信息多
  • +
+

缺点:

+
    +
  • 没有图形化展示
  • +
  • 实时性差,第二天才更新
  • +
+

Butterfly-gitcalendar

结合 github-calendar.jsGitHub Contribution Calendar API

+

参考地址:

+ +

优势:

+
    +
  • 自定义样式与弹窗
  • +
  • 有数据返回,可读信息多
  • +
  • 汉化
  • +
+

缺点:

+
    +
  • 实时性差,第二天才更新
  • +
+

实现方案

本次以最后一种解决方案 Butterfly-gitcalendar 为基础,进行了部分更改

+

步骤一:新增gitcalendar.pug页面

项目根目录/themes/hexo-theme-butterfly-3.3.0/layout/文件夹下新建gitcalendar.pug文件,并复制以下代码

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
#calendar.calendar
#gitmessage(:style='{top:y+px,left:x+px,position: fixed,zIndex:9999}')
.angle-wrapper
span {{span1}} &nbsp;
span {{span2}} 次提交
.position-relative
.border.py-2.graph-before-activity-overview
.js-calendar-graph.mx-md-2.mx-3.d-flex.flex-column.flex-items-end.flex-xl-items-center.overflow-hidden.pt-1.is-graph-loading.graph-canvas.calendar-graph.height-full.text-center(data-graph-url='/users/LeeDebug/contributions?to=2020-10-29', data-url='/LeeDebug', data-from='2019-10-27 00:00:00 UTC', data-to='2020-10-29 23:59:59 UTC', data-org)
#calendarcanvasbox(v-if='simplemode')
canvas#gitcanvas(style='animation: none;')
svg.js-calendar-graph-svg(width='100%', viewBox='0 0 770 128', v-if='!simplemode')
text.month(:x='32 + monthindex*64', y='20', v-for='(month,monthindex) in monthchange') {{month}}
text.wday(text-anchor='start', dx='0', dy='40') 日
text.wday(text-anchor='start', dx='0', dy='65') 二
text.wday(text-anchor='start', dx='0', dy='90') 四
text.wday(text-anchor='start', dx='0', dy='115') 六
g(v-for='(weekitem,weekIndex) in data', :transform='\'translate(\'+ (16 + weekIndex*14) + \',\' + \'0)\'')
rect(@mouseover="selectStyle(dayitem,$event)" @mouseleave="outStyle()" v-for='(dayitem,dayIndex) in weekitem', :style='{fill:thiscolor(dayitem.count),shapeRendering:crispedges}', :data-score='dayitem.count', :data-date='dayitem.date', x='0', :y=' 30 + dayIndex*13 ', width='11', height='11')
.contrib-footer.clearfix.mt-1.mx-3.px-3.pb-1
.float-left.text-gray
| 数据来源
a(:href="'https://github.com/'+ user ", target='blank') @{{user}}
.contrib-legend.text-gray(title='A summary of pull requests, issues opened, and commits to the default and gh-pages branches.')
| Less

ul.legend
li(:style='{backgroundColor:leeGreen[0]}')
li(:style='{backgroundColor:leeGreen[1]}')
li(:style='{backgroundColor:leeGreen[2]}')
li(:style='{backgroundColor:leeGreen[3]}')
li(:style='{backgroundColor:leeGreen[4]}')
| More

.contrib-column.contrib-column-first.table-column
span.text-muted 过去一年提交
span.contrib-number {{total}}
span.text-muted.data-range {{oneyearbeforeday}}&nbsp;-&nbsp;{{thisday}}
.contrib-column.table-column
span.text-muted 最近一月提交
span.contrib-number {{thisweekdatacore}}
span.text-muted.data-range {{amonthago}}&nbsp;-&nbsp;{{thisday}}
.contrib-column.table-column
span.text-muted 最近一周提交
span.contrib-number {{weekdatacore}}
span.text-muted.data-range {{aweekago}}&nbsp;-&nbsp;{{thisday}}
+ +

注:请将代码中的两处LeeDebug替换为你自己的github用户名

+

步骤二:在首页引用gitcalendar.pug

请打开项目根目录/themes/hexo-theme-butterfly-3.3.0/layout/index.pug页面,添加下面三行代码(如有顾虑,请自行备份)

+
1
2
3
4
5
6
7
8
9
10
  extends includes/layout.pug

block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
+ .recent-post-item(style='width:100%; height: auto;')
+ include gitcalendar.pug
+ .recent-post-item(style='height:0px; clear:both; margin-top: 0px;')
+postUI
include includes/pagination.pug
+ +

前两行为引用gitcalendar.pug页面,第3行是为了清除上面的布局样式;如有影响,请删除即可

+

步骤三:新增gitcalendar.js脚本

项目根目录/themes/hexo-theme-butterfly-3.3.0/source/下,新建文件夹并命名为gitcalendar,在此目录下新建文件夹并命名为js,在此目录下新建js文件并命名为gitcalendar.js(为了区分版本,个人命名为gitcalendar_v01.js),并复制以下代码

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
const calendar = new Vue({
el: '#calendar',
data: {
// 打开时使用canvas绘制gitcalendar,关闭时使用svg绘制gitcalendar
// canvas:dom数少,但图像会发生模糊,自适应一般 svg:dom数多,图像清晰,自适应更佳
simplemode: false,
// 这里填写你的github用户名
user: 'LeeDebug',
fixed: 'fixed',
px: 'px',
x: '',
y: '',
span1: '',
span2: '',
month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
monthchange: [],
oneyearbeforeday: '',
thisday: '',
amonthago: '',
aweekago: '',
weekdatacore: 0,
datacore: 0,
total: 0,
datadate: '',
data: [],
positionplusdata: [],
firstweek: [],
lastweek: [],
beforeweek: [],
thisweekdatacore: 0,
mounthbeforeday: 0,
mounthfirstindex: 0,
crispedges: 'crispedges',
thisdayindex: 0,
amonthagoindex: 0,
amonthagoweek: [],
firstdate: [],
first2date: [],
montharrbefore: [],
monthindex: 0,
leeGreen: ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'], // github的强度颜色
purple: ['#ebedf0', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f',],
green: ['#ebedf0', '#f0fff4', '#dcffe4', '#bef5cb', '#85e89d', '#34d058', '#28a745', '#22863a', '#176f2c', '#165c26', '#144620'],
blue: ['#ebedf0', '#f1f8ff', '#dbedff', '#c8e1ff', '#79b8ff', '#2188ff', '#0366d6', '#005cc5', '#044289', '#032f62', '#05264c',],
color: ['#ebedf0', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f',]
},
methods: {
selectStyle(data, event) {
$('.angle-wrapper').show();
this.span1 = data.date;
this.span2 = data.count;
this.x = event.clientX - 100;
this.y = event.clientY - 60
},
outStyle() {
$('.angle-wrapper').hide()
},
thiscolor(x) {
if (x === 0) {
return this.leeGreen[0]
} else if (x <= 5) {
return this.leeGreen[1]
} else if (x <= 10) {
return this.leeGreen[2]
} else if (x <= 18) {
return this.leeGreen[3]
} else {
return this.leeGreen[4]
}
},
}
});
let githubapiurl = "https://githubapi.ryanchristian.dev/user/" + calendar.user;
$(function () {
$.ajax({
type: "GET",
url: githubapiurl,
dataType: "json",
success: function (data) {
;
calendar.data = data.contributions;
calendar.total = data.total;
calendar.first2date = calendar.data[48];
calendar.firstdate = calendar.data[47];
calendar.firstweek = data.contributions[0];
calendar.lastweek = data.contributions[52];
calendar.beforeweek = data.contributions[51];
calendar.thisdayindex = calendar.lastweek.length - 1;
calendar.thisday = calendar.lastweek[calendar.thisdayindex].date;
calendar.oneyearbeforeday = calendar.firstweek[0].date;
calendar.monthindex = calendar.thisday.substring(5, 7) * 1;
calendar.montharrbefore = calendar.month.splice(calendar.monthindex, 12 - calendar.monthindex);
calendar.monthchange = calendar.montharrbefore.concat(calendar.month);
addweek();
addlastmonth();

function responsiveChart() {
let c = document.getElementById("gitcanvas");
let cmessage = document.getElementById("gitmessage");
let ctx = c.getContext("2d");
c.width = document.getElementById("calendarcanvasbox").offsetWidth;
let linemaxwitdh = 0.96 * c.width / calendar.data.length;
c.height = 9 * linemaxwitdh;
let lineminwitdh = 0.8 * linemaxwitdh;
let setposition = {
x: 0.02 * c.width,
y: 0.025 * c.width
};
for (let week in calendar.data) {
weekdata = calendar.data[week];
for (let day in weekdata) {
let dataitem = {date: "", count: "", x: 0, y: 0};
calendar.positionplusdata.push(dataitem);
ctx.fillStyle = calendar.thiscolor(weekdata[day].count);
// ctx.fillStyle = calendar.leeGreen[weekdata[day].intensity]; // 可按api的强度直接取色
setposition.y = Math.round(setposition.y * 100) / 100;
dataitem.date = weekdata[day].date;
dataitem.count = weekdata[day].count;
dataitem.x = setposition.x;
dataitem.y = setposition.y;
ctx.fillRect(setposition.x, setposition.y, lineminwitdh, lineminwitdh);
setposition.y = setposition.y + linemaxwitdh
}
;
setposition.y = 0.025 * c.width;
setposition.x = setposition.x + linemaxwitdh
}
;
ctx.font = "600 Arial";
ctx.fillStyle = '#aaa';
ctx.fillText("日", 0, 1.9 * linemaxwitdh);
ctx.fillText("二", 0, 3.9 * linemaxwitdh);
ctx.fillText("四", 0, 5.9 * linemaxwitdh);
ctx.fillText("六", 0, 7.9 * linemaxwitdh);
let monthindexlist = c.width / 24;
for (let index in calendar.monthchange) {
ctx.fillText(calendar.monthchange[index], monthindexlist, 0.7 * linemaxwitdh);
monthindexlist = monthindexlist + c.width / 12
}
;
cmessage.onmousemove = function (event) {
$('.angle-wrapper').hide()
};
c.onmousemove = function (event) {
$('.angle-wrapper').hide()
getMousePos(c, event);
};

function getMousePos(canvas, event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left * (canvas.width / rect.width);
var y = event.clientY - rect.top * (canvas.height / rect.height);
for (let item of calendar.positionplusdata) {
let lenthx = x - item.x;
let lenthy = y - item.y;
if (0 < lenthx && lenthx < lineminwitdh) {
if (0 < lenthy && lenthy < lineminwitdh) {
$('.angle-wrapper').show();
calendar.span1 = item.date;
calendar.span2 = item.count;
calendar.x = event.clientX - 100;
calendar.y = event.clientY - 60
}
}
}
}
}

responsiveChart();
$(window).on('resize', responsiveChart);
window.onscroll = function () {
$('.angle-wrapper').hide()
};
console.log(calendar.positionplusdata)

function addlastmonth() {
if (calendar.thisdayindex === 0) {
thisweekcore(52);
thisweekcore(51);
thisweekcore(50);
thisweekcore(49);
thisweekcore(48);
calendar.thisweekdatacore += calendar.firstdate[6].count;
calendar.amonthago = calendar.firstdate[6].date
} else {
thisweekcore(52);
thisweekcore(51);
thisweekcore(50);
thisweekcore(49);
thisweek2core();
calendar.amonthago = calendar.first2date[calendar.thisdayindex - 1].date
}
};

function thisweek2core() {
for (let i = calendar.thisdayindex - 1; i < calendar.first2date.length; i++) {
calendar.thisweekdatacore += calendar.first2date[i].count
}
};

function thisweekcore(index) {
for (let item of calendar.data[index]) {
calendar.thisweekdatacore += item.count
}
};

function addlastweek() {
for (let item of calendar.lastweek) {
calendar.weekdatacore += item.count
}
};

function addbeforeweek() {
for (let i = calendar.thisdayindex; i < calendar.beforeweek.length; i++) {
calendar.weekdatacore += calendar.beforeweek[i].count
}
};

function addweek() {
if (calendar.thisdayindex === 6) {
calendar.aweekago = calendar.lastweek[0].date;
addlastweek()
} else {
lastweek = data.contributions[51];
calendar.aweekago = lastweek[calendar.thisdayindex + 1].date;
addlastweek();
addbeforeweek()
}
}
}
})
});
if(document.getElementById("calendarcanvasbox").offsetWidth<500){calendar.simplemode=false}
+ +

注:同理,请将代码中的一处LeeDebug替换为你自己的github用户名

+

步骤四:新增gitcalendar.css样式

在步骤三新增的gitcalendar目录下,即项目根目录/themes/hexo-theme-butterfly-3.3.0/source/gitcalendar下,新建文件夹并命名为css,在此目录下新建css文件并命名为gitcalendar.css(同理,为了区分版本,个人命名为gitcalendar_v01.css),并复制以下代码

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
.calendar {
font-family: SourceHanSans-Medium;
border: 1px solid #DDDDDD;
border-radius: 3px;
min-height: 120px;
text-align: center;
margin: 0 auto;
border-width:0px;
width:100%;
display: flex;
display: -webkit-flex;
justify-content: center;
align-items:center;
flex-wrap:wrap;
}
.calendar-graph text.wday,
.calendar-graph text.month {
font-size: 10px;
fill: #aaa;
}
.contrib-legend {
text-align: right;
padding: 0 14px 10px 0;
display: inline-block;
float: right;
}
.contrib-legend .legend {
display: inline-block;
list-style: none;
margin: 0 5px;
position: relative;
bottom: -1px;
padding: 0;
}
.contrib-legend .legend li {
display: inline-block;
width: 10px;
height: 10px;
}
.text-small {
font-size: 12px;
color: #767676;
}
.calendar-graph {
padding: 15px 0 0;
text-align: center;
}
.contrib-column {
text-align: center;
border-left: 1px solid #ddd;
border-top: 1px solid #ddd;
font-size: 11px;
}
.contrib-column-first {
border-left: 0;
}
.table-column {
padding:10px;
display: table-cell;
width:33%;
vertical-align: top;
}
.contrib-number {
font-weight: 300;
line-height: 1.3em;
font-size: 24px;
display: block;
}
.calendar img.spinner {
width: 70px;
margin-top: 50px;
min-height: 70px;
}
.monospace {
text-align: center;
color: #000;
font-family: monospace;
}
.monospace a {
color: #1D75AB;
text-decoration: none;
}
.contrib-footer {
font-size: 12px;
padding: 0 12px 12px;
text-align: left;
width: 100%;
box-sizing: border-box;
height: 26px;
}
.left.text-muted {
float: left;
margin-left: 9px;
color: #767676;
}
.left.text-muted a {
color: #4078c0;
text-decoration: none;
}
.left.text-muted a:hover,
.monospace a:hover {
text-decoration: underline;
}
h2.f4.text-normal.mb-3 {
display: none;
}

.float-left.text-gray {
float: left;
}
#user-activity-overview{
display:none;
}
.day-tooltip {
white-space: nowrap;
position: absolute;
z-index: 99999;
padding: 10px;
font-size: 12px;
color: #959da5;
text-align: center;
background: rgba(0,0,0,.85);
border-radius: 3px;
display: none;
pointer-events: none;
}
.day-tooltip strong {
color: #dfe2e5;
}
.day-tooltip.is-visible {
display: block;
}
.day-tooltip:after {
position: absolute;
bottom: -10px;
left: 50%;
width: 5px;
height: 5px;
box-sizing: border-box;
margin: 0 0 0 -5px;
content: " ";
border: 5px solid transparent;
border-top-color: rgba(0,0,0,.85)
}
.position-relative {
width:100%;
padding-left:20px;
padding-right:20px;
}
@media screen and (max-width: 650px){
.contrib-footer{
padding: 0;
}
.contrib-column{
/* display:none */
line-height: initial;
padding: 5px;
}
.contrib-column .contrib-number{
font-size: 14px;
font-weight: 500;
}
.contrib-column .data-range{
display:none
}
}
.angle-wrapper {
z-index:9999;
display:inline;
display:none;
width: 200px;
height: 40px;
position: relative;
padding: 5px 0;
background: rgba(0, 0, 0, 0.8);
border-radius: 8px;
text-align: center;
color: white;
}
.angle-box {
position:fixed;
padding:10px
}
.angle-wrapper span{
padding-bottom:1em;
}
.angle-wrapper:before {
content: '';
width: 0;
height: 0;
border: 10px solid transparent;
border-top-color: rgba(0, 0, 0, 0.8);
position: absolute;
left: 47.5%;
top: 100%;
}
+ +

引入js、css代码,即vue的依赖

项目根目录/themes/hexo-theme-butterfly-3.3.0/_config.yml文件内,找到inject处,并添加以下3行代码

+
1
2
3
4
5
6
inject:
head:
+ - <link rel="stylesheet" href="/gitcalendar/css/gitcalendar_v01.css"/>
bottom:
+ - <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
+ - <script src="/gitcalendar/js/gitcalendar_v01.js"></script>
+ +

注:此处的js和css文件也可改为cdn引入,但要注意缓存问题。另,如无特殊需求,请引入vue@2.6.11,非必要请不要修改

+

步骤五:打包部署

教程到此结束,打包部署即可查看效果。如有疑问请联系博主

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/14/git%E8%B4%A1%E7%8C%AE%E5%A2%99gitcalendar/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git a/2020/12/15/GitHub-Corners/index.html b/2020/12/15/GitHub-Corners/index.html new file mode 100644 index 000000000..e5f52ce17 --- /dev/null +++ b/2020/12/15/GitHub-Corners/index.html @@ -0,0 +1,175 @@ +GitHub Corners | 淳淳同学的个人博客 + + + + + + + + + + + + +

GitHub Corners

+

可以在你的博客或者自己的网站上增加一个github的标志链接

+
+

GitHub Ribbons

github repo是最初的角标链接,像丝带一样的长条形状,如下图所示:

+

GitHub Ribbons

+

接入代码如下所示:

+
1
<a href="https://github.com/you"><img loading="lazy" width="149" height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_darkblue_121621.png?resize=149%2C149" class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
+ +

GitHub Corners

GitHub Corners是一款更为简单干净的角标链接,如本站右上角所示:

+

接入代码如下所示:

+
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
<a href="https://your-url" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<style>
.github-corner:hover .octo-arm{
animation:octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave{
0%,100%{
transform:rotate(0);
}
20%,60%{
transform:rotate(-25deg);
}
40%,80%{
transform:rotate(10deg);
}
}
@media (max-width:500px){
.github-corner:hover .octo-arm{
animation:none;
}
.github-corner .octo-arm{
animation:octocat-wave 560ms ease-in-out;
}
}
</style>
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/15/GitHub-Corners/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/19/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210/index.html" "b/2020/12/19/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210/index.html" new file mode 100644 index 000000000..ea2b52529 --- /dev/null +++ "b/2020/12/19/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210/index.html" @@ -0,0 +1,205 @@ +qiankun的css样式污染解决方案 | 淳淳同学的个人博客 + + + + + + + + + + + + +

qiankun的css样式污染解决方案

+

在使用qiankun框架做微前端开发的过程中,遇到了诸多难题,比如路由重定向、变量名及事件名冲突、挂载注销机制及生命周期、keep-alive等,目前遇到的比较难解决的问题可能就是css样式污染问题了。这次抽出了几天时间研究了一下,遂总结此文

+
+

问题概述

在qiankun加载子应用后,主子应用的样式之间会产生污染,常见的css样式污染有以下几种情况:

+
    +
  • 无论是否进行样式隔离:主应用的样式污染了子应用(原因:主应用的样式添加了 !important 属性或 >>> 穿透属性)
  • +
  • 未进行样式隔离:子应用的样式污染了主应用(原因:样式重名,后加载的优先级高)
  • +
  • 处理过样式隔离:子应用打开的弹窗、抽屉、popover等这种需要插入到主应用body的dom元素,样式丢失或应用了主项目的样式(原因:开启沙箱时,子应用的样式作用域只在子应用内,但如描述,子应用的dom被插入到了主应用的body中,遂出现了此种情况)
  • +
+

qiankun自带的css沙箱

个人理解,qiankun加载子项目css样式机制大体为:挂载子应用时将子应用的css样式以style标签的形式插入并做快照,卸载子应用时再将快照内的style样式删除。所以在加载子应用期间,若未开启css沙箱隔离,后加载的这些样式,可能会对整个系统的样式产生影响,对此,qiankun提供了两种css沙箱功能,可以将子应用的样式包裹在沙箱容器内部,以此来达到样式隔离的目的。大体代码如下所示:

+
1
2
3
4
5
6
7
8
9
this.microApp = loadMicroApp(
{ apps infos ... },
{
sandbox: {
strictStyleIsolation: true // 严格沙箱
experimentalStyleIsolation: true // 实验性沙箱
}
}
)
+ +
    +
  1. 严格沙箱
  2. +
+

在加载子应用时,添加strictStyleIsolation: true属性,实现形式为将整个子应用放到Shadow DOM内进行嵌入,完全隔离了主子应用

+

缺点:

+
    +
  • 子应用的弹窗、抽屉、popover因找不到主应用的body会丢失,或跑到整个屏幕外(具体原因作者并未详细研究)
  • +
  • 主应用不方便去修改子应用的样式
  • +
+
    +
  1. 实验性沙箱
  2. +
+

在加载子应用时,添加experimentalStyleIsolation: true属性,实现形式类似于vue中style标签中的scoped属性,qiankun会自动为子应用所有的样式增加后缀标签,如:div[data-qiankun-microName]

+

缺点:

+
    +
  • 子应用的弹窗、抽屉、popover因插入到了主应用的body,所以导致样式丢失或应用了主应用了样式
  • +
+

最终解决方案

说了这么多qiankun自带的css沙箱隔离,但都有各自的缺点,并且对于系统的实现上,影响范围还比较严重,代码的修改范围也比较大。作者的项目中,主子应用都是vue项目,并且都用了element家的样式且都各自魔改过,遂果断不开启css沙箱,给各自的项目class全局加上一个各自的命名空间,可以自己的项目名,比如:myVue bodymyVue el-form-info__labelmyVue customClass等。

+

代码实现

    +
  1. 添加依赖
  2. +
+
1
→ npm i postcss-plugin-namespace -D
+ +
    +
  1. 配置postcss
  2. +
+

在项目根目录创建postcss.config.js文件,并复制以下内容:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
// console.log('=> => => postcss.config.js start => => =>')
module.exports = (ctx) => {
return {
plugins: [
require('postcss-plugin-namespace')('#lee_project', {
ignore: [
'html', /body/, 'span', 'el-form-item'
]
}),
]
}
}
// console.log('=> => => postcss.config.js end => => =>')
+ +

该插件会将全局所有class前加上统一前缀,并过滤掉ignore内的标签;ignore内可以写字符串,可以写正则表达式。但每次编译前都会运行,所以可能会增加编译时间,所以日常开发环境下可以将此文件名随便改成别的,上线前记得改回来调试一下(如果直接隐藏掉代码的话,只要有postcss.config.js这个文件webpack会自动帮你执行,并且会提示你的postcss啥也没干,也相当于每次都走了这个脚本)。

+

注意:如果用/body/这样的正则,会将所有带body的class都过滤掉,比如el-drawer__bodyel-dialog__body等。

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/19/qiankun%E7%9A%84css%E6%B2%99%E7%AE%B1%E9%9A%94%E7%A6%BB%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/26/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211/index.html" "b/2020/12/26/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211/index.html" new file mode 100644 index 000000000..fb1d8dffa --- /dev/null +++ "b/2020/12/26/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211/index.html" @@ -0,0 +1,170 @@ +git仓库推送脚本(本地简易版) | 淳淳同学的个人博客 + + + + + + + + + + + + +

git仓库推送脚本(本地简易版)

+

由于每次写完代码都要执行一大堆命令来将本地代码推送至远程仓库,所以索性写一个sh脚本

+
+

新建push.sh文件:

在项目主目录,即/git所在目录新建push.sh文件,并复制以下内容:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
echo → 暂存选取所有代码
git add .

read -p "→ 请输入您的commit提交信息:" MSG

echo → 提交所有暂存代码
git commit -m "$MSG"

echo → 将代码推送至三端git仓库
git push -u all master

# 如果不是hexo项目可忽略以下内容
echo → Hexo自动构建及部署
npm run clean
npm run build
npm run deploy
+ +

运行脚本

每次推送时,在当前目录运行sh push.sh命令即可,接下来会提示你输入要提交的信息,输入完点击回车即可

+

注:如果加入了hexo项目的构建部署命令,每次推送时也会帮你完成部署

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/26/git%E4%BB%93%E5%BA%93%E6%8E%A8%E9%80%81%E8%84%9A%E6%9C%AC%EF%BC%88%E6%9C%AC%E5%9C%B0%E7%AE%80%E6%98%93%E7%89%88%EF%BC%89/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2020/12/27/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220/index.html" "b/2020/12/27/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220/index.html" new file mode 100644 index 000000000..190e418f5 --- /dev/null +++ "b/2020/12/27/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220/index.html" @@ -0,0 +1,214 @@ +vue3的生命周期钩子 | 淳淳同学的个人博客 + + + + + + + + + + + + +

vue3的生命周期钩子

+

vue3的生命周期钩子与vue2的差不多,只是命名和调用上有一些诧异,详见下文

+
+

与 2.x 版本生命周期相对应的 Composition API

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Vue 2.xVue 3.x
beforeCreate改用 setup()
created改用 setup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
errorCapturedonErrorCaptured
+

新增的钩子函数

除了和 2.x 生命周期等效项之外,Composition API 还提供了以下调试钩子函数:

+
    +
  • onRenderTracked
  • +
  • onRenderTriggered
  • +
+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2020/12/27/vue3%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/01/08/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266/index.html" "b/2021/01/08/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266/index.html" new file mode 100644 index 000000000..9cbfec03e --- /dev/null +++ "b/2021/01/08/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266/index.html" @@ -0,0 +1,169 @@ +copy-webpack-plugin处理单独js文件 | 淳淳同学的个人博客 + + + + + + + + + + + + +

copy-webpack-plugin处理单独js文件

+

当文件在static或public目录下但又想对文件进行编译处理,即可在此插件中进行配置

+
+

使用说明

copy-webpack-plugin是webpack自带的插件,本意是将某个文件/文件夹,从dir1处复制到dist下,即当你在运行npm run build时,static或public目录下的文件就是走的此插件

+

配置信息

因为我使用的是基于@vue/cli-servicevue3.x,所以是在vue.config.js中设置(如果是vue2.x的版本,请在webpack.base.js中设置)

+
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
33
34
35
36
37
// 引入
const CopyWebpackPlugin = require('copy-webpack-plugin')
const UglifyJS = require('uglify-js')

// 使用
let config = {
// ...others,
configureWebpack: config => {
config.plugins.push(
// see document: https://www.npmjs.com/package/copy-webpack-plugin/v/5.1.2
new CopyWebpackPlugin([
{
from: resolve('./public/handle.js'), // 文件名或目录
to: './[name].[contenthash].[ext]', // 文件名后添加hash值
transform(content, path) { // 修改文件内容
const code = UglifyJS.minify(content.toString()).code;
return code;
},
transformPath(targetPath, absolutePath) { // 修改文件路径:目标路径、源路径
newHashPath = targetPath;
return targetPath;
},
},
{
from: resolve('./public/index2.html'),
to: './[name].[ext]',
transform(content, path) {
let code = UglifyJS.minify(content.toString()).code;
return code;
},
force: true, // 覆盖已经存在的文件
},
])
);
},
}
module.exports = config;
+ +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/01/08/copy-webpack-plugin%E5%A4%84%E7%90%86%E5%8D%95%E7%8B%ACjs%E6%96%87%E4%BB%B6/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/01/17/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250/index.html" "b/2021/01/17/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250/index.html" new file mode 100644 index 000000000..744c651e8 --- /dev/null +++ "b/2021/01/17/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250/index.html" @@ -0,0 +1,170 @@ +安卓微信浏览器中type=file的input框无法使用 | 淳淳同学的个人博客 + + + + + + + + + + + + +

安卓微信浏览器中type=file的input框无法使用

+

如果使用html的input框设为type=file来选择文件,在安卓的微信中打开页面时,会提示暂无可使用应用等错误提示

+
+

解决方案

若要选择图片,需将input框的accept属性设为image/*;若要选择文件,需将此属性设为

+
1
2
3
4
5
6
7
<input
v-show="false"
:ref="el => { if (el) uploadRefs = el }"
type="file"
:accept="acceptFileTypeArr"
@change="handleUploadFunc"
/>
+
1
2
3
4
5
6
7
8
9
const acceptFileTypeArr: any = computed(() => { // 当前支持上传类型
if (uploadType.value === 'image') {
return 'image/*';
}
if (uploadType.value === 'file') {
return '';
}
return '';
});
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/01/17/%E5%AE%89%E5%8D%93%E5%BE%AE%E4%BF%A1%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%ADtype-file%E7%9A%84input%E6%A1%86%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/01/26/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256/index.html" "b/2021/01/26/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256/index.html" new file mode 100644 index 000000000..70100e69e --- /dev/null +++ "b/2021/01/26/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256/index.html" @@ -0,0 +1,170 @@ +修改element-ui的全局配置 | 淳淳同学的个人博客 + + + + + + + + + + + + +

修改element-ui的全局配置

+

项目因使用qiankun嵌入了子项目,且都使用了element-ui的弹窗样式,遂导致子应用中插入到父应用body的弹窗因层级过低无法展示

+
+

配置信息

element-ui的弹窗默的z-index层级默认是从2000开始的,并且打开多个弹窗时z-index会逐步递增,所以若主子应用不做区分的话,很可能会导致有的弹窗被遮挡

+

好在element提供了入口可将全局的弹窗层级设置一个起始值,即只需将子应用的层级初始值调高即可

+
1
2
3
4
5
6
7
// 在main.ts中
import Vue from 'vue';
import Element from 'element-ui';
Vue.use(Element, {
size: 'small', // 组件的默认尺寸 mini、small、medium
zIndex: 3000 // 弹窗默认层级初始值,默认2000
});
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/01/26/%E4%BF%AE%E6%94%B9element-ui%E7%9A%84%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/02/06/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266/index.html" "b/2021/02/06/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266/index.html" new file mode 100644 index 000000000..6807c3019 --- /dev/null +++ "b/2021/02/06/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266/index.html" @@ -0,0 +1,172 @@ +viewerjs查看大图组件 | 淳淳同学的个人博客 + + + + + + + + + + + + +

viewerjs查看大图组件

+

因为项目没有使用element-ui,所以不能使用自带的el-image组件进行大图查看,遂找了一个单独的组件

+
+

安装

1
npm install viewerjs
+ +

使用案例

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 单个图片查看 -->
<div>
<img id="image" src="picture.jpg" alt="Picture">
</div>

<!-- 多个图片查看 -->
<div>
<ul id="images">
<li><img src="picture-1.jpg" alt="Picture 1"></li>
<li><img src="picture-2.jpg" alt="Picture 2"></li>
<li><img src="picture-3.jpg" alt="Picture 3"></li>
</ul>
</div>
+ +
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
// see document: https://github.com/fengyuanchen/viewerjs/blob/master/README.md
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
let viewer: any;
export function initImageViewer(thumbnail: string = '') {
// 获取最新的消息框实例
const msgDom: any = (document as any).getElementById('messageBox');
// 已加载过
if (viewer) {
// 更新实例里的图片源
viewer.update(msgDom);
} else { // 第一次加载
viewer = new Viewer(msgDom, {
// 只筛选出图片消息
filter(image: any) {
const isImgMsg = image.className.indexOf('imageMsg') > -1;
return isImgMsg;
},
// 去掉url中的缩略图参数
url(image: any) {
return image.src.replace(thumbnail, '');
},
});
}
}

+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/02/06/viewerjs%E6%9F%A5%E7%9C%8B%E5%A4%A7%E5%9B%BE%E7%BB%84%E4%BB%B6/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/02/10/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217/index.html" "b/2021/02/10/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217/index.html" new file mode 100644 index 000000000..8a38c2429 --- /dev/null +++ "b/2021/02/10/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217/index.html" @@ -0,0 +1,173 @@ +实现输入框的n种形式 | 淳淳同学的个人博客 + + + + + + + + + + + + +

实现输入框的n种形式

+

开发过程中遇到的新方案

+
+

textarea

最常见的就是这种形式,不多赘述。代码如下

+

但要展示带html元素的内容,可能就不是那么方便了

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<textarea
id="sendMsgBox"
class="sendMsgBox"
ref="sendMsgBox"
row="3"
maxlength="1000"
v-model="sendContent"
:readonly="cantOperate"
:placeholder="getPlaceholder"
@blur="handleBlurFun"
@focus="handleFocusFun"
@keydown="handleKeyDown($event)"
:style="{ 'height': textareHeight }"
></textarea>
+ +

pre标签

contenteditable属性会将该段落变为可编辑状态,如同直接用vue的v-html展示。代码如下

+
1
2
3
<pre contenteditable="true">
可以直接在这里输入.....
</pre>
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/02/10/%E5%AE%9E%E7%8E%B0%E8%BE%93%E5%85%A5%E6%A1%86%E7%9A%84n%E7%A7%8D%E5%BD%A2%E5%BC%8F/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/02/17/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257/index.html" "b/2021/02/17/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257/index.html" new file mode 100644 index 000000000..ceb2a99cf --- /dev/null +++ "b/2021/02/17/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257/index.html" @@ -0,0 +1,169 @@ +navigator.userAgent获取当前设备信息 | 淳淳同学的个人博客 + + + + + + + + + + + + +

navigator.userAgent获取当前设备信息

+

获取当前是何设备,来区分不同的事件及渲染模式

+
+

navigator.userAgent

封装好函数直接调用,利用switch直接进行判断即可

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export function currDevice() {
const u = navigator.userAgent;
const app = navigator.appVersion; // appVersion 可返回浏览器的平台和版本信息。该属性是一个只读的字符串。
const browserLang = (navigator.browserLanguage || navigator.language).toLowerCase(); //获取浏览器语言
const deviceBrowser = (() => {
return {
trident: u.indexOf('Trident') > -1, // IE内核
presto: u.indexOf('Presto') > -1, // opera内核
webKit: u.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1, // 火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.Mac OS X/), // ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, // android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, // 是否iPad
webApp: u.indexOf('Safari') === -1, // 是否web应用程序,没有头部和底部
weixin: u.indexOf('MicroMessenger') > -1, // 是否微信
qq: u.match(/\sQQ/i) === "qq", // 是否QQ
};
})();
return deviceBrowser;
}
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/02/17/navigator.userAgent%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E8%AE%BE%E5%A4%87%E4%BF%A1%E6%81%AF/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/02/21/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230/index.html" "b/2021/02/21/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230/index.html" new file mode 100644 index 000000000..83665aa2a --- /dev/null +++ "b/2021/02/21/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230/index.html" @@ -0,0 +1,177 @@ +linear-gradient函数实现线性渐变 | 淳淳同学的个人博客 + + + + + + + + + + + + +

linear-gradient函数实现线性渐变

+

当tab标签超出滚动时,在侧边增加一个渐变透明的框,会使视觉上更加明显,让用户感知到该处可以滑动

+
+

效果对比展示

原本样式:

+

原本样式

+

增加渐变的透明框后的样式:

+

增加渐变的透明框后的样式

+

滚动后的效果:

+

滚动后的效果

+

可滑动的tab标签

1
2
3
4
5
6
7
8
9
/* 超出部分可所有滑动 */
.tab_scroll {
display: -webkit-box;
overflow: auto;
}
/* 隐藏滚轮 */
.tab_scroll::-webkit-scrollbar {
display: none;
}
+ + +

渐变透明的小方块

1
2
3
4
5
6
7
8
9
10
.tab_scroll:before {
content: '';
position: absolute;
right: 0;
float: right;
/* border: 1px solid red; */
height: 66px;
width: 60px;
background: linear-gradient(to right, transparent, white);
}
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/02/21/linear-gradient%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0%E7%BA%BF%E6%80%A7%E6%B8%90%E5%8F%98/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/03/01/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260/index.html" "b/2021/03/01/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260/index.html" new file mode 100644 index 000000000..908245212 --- /dev/null +++ "b/2021/03/01/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260/index.html" @@ -0,0 +1,173 @@ +查看代码行数 | 淳淳同学的个人博客 + + + + + + + + + + + + +

查看代码行数

+

项目写久了,就像看看自己一个项目的真实代码有多少行,所以找了下面两个方法

+
+

命令行查看

用mac终端自带的的find命令,可以查看目录下每个文件的行数,及最后输出总行数

+
1
2
3
4
5
6
7
8
# input >
find . "(" -name "*.vue" -or -name "*.html" -or -name "*.ts" -or -name "*.js" ")" -print | xargs wc -l

# output >
423 a.js
1842 b.vue
52 c.html
7253 total
+ + +

VsCode查看

直接在vscode的全局搜索中(快捷键为Command + Shift + c),输入b*[^:b#/]+.*$,并使用Use Regular Expression选项(即最右侧的星号和方块的按钮),进行搜索,即可查看

+
1
2
3
4
5
6
7
8
9
# input >
b*[^:b#/]+.*$

# output >
7253 results in 77 files

423 a.js
1842 b.vue
52 c.html
+ +

VsCode查看代码行数

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/03/01/%E6%9F%A5%E7%9C%8B%E4%BB%A3%E7%A0%81%E8%A1%8C%E6%95%B0/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/03/08/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256/index.html" "b/2021/03/08/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256/index.html" new file mode 100644 index 000000000..3be9f7c9a --- /dev/null +++ "b/2021/03/08/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256/index.html" @@ -0,0 +1,178 @@ +加载聊天历史记录并保留滚动条当前位置 | 淳淳同学的个人博客 + + + + + + + + + + + + +

加载聊天历史记录并保留滚动条当前位置

+

在聊天框中,加载历史消息肯定是往消息的上面去加载,即对应数组的Array.unshift()操作,此时默认滚动条会回到顶部,所以我们需要重置滚动条的位置

+
+

Vue中代码实现

首先记录加载历史前,滚动条的位置scrollHeight

+
1
const scrollHeight: number = (document as any).getElementById('messageBox').scrollHeight;
+ +

加载历史消息

+
1
2
3
4
for (let i = 0; i < outMsg.list.length; i++) {
// todo sth, ex: 消息处理
activeList.unshift(list[i]);
}
+ +

滚动到原来的位置

+

注:因为我是在vue环境下,需要确保页面已经渲染完,再滚动到加载前的位置,所以使用Vue.nextTick()

+
1
2
3
4
nextTick(() => {
const scrollDom: any = (document as any).getElementById('messageBox');
scrollDom.scrollTop = (scrollDom.scrollHeight - scrollHeight);
});
+ +

jQuery代码实现

原理同上,直接上代码

+
1
2
var scrollHeight = document.getElementById("messageBox").scrollHeight;
$("#messageBox").scrollTop(document.getElementById("messageBox").scrollHeight - scrollHeight);
+ +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/03/08/%E5%8A%A0%E8%BD%BD%E8%81%8A%E5%A4%A9%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E5%B9%B6%E4%BF%9D%E7%95%99%E6%BB%9A%E5%8A%A8%E6%9D%A1%E5%BD%93%E5%89%8D%E4%BD%8D%E7%BD%AE/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/03/17/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206/index.html" "b/2021/03/17/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206/index.html" new file mode 100644 index 000000000..0a6e3c2c3 --- /dev/null +++ "b/2021/03/17/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206/index.html" @@ -0,0 +1,255 @@ +【转】看完让你彻底搞懂Websocket原理 | 淳淳同学的个人博客 + + + + + + + + + + + + +

【转】看完让你彻底搞懂Websocket原理

+

最近在学习websocket的时候,从知乎的《WebSocket 是什么原理?为什么可以实现持久连接?》问题下看到了这个回帖,对我的帮助比较大,遂转载到自己的博客

+
+

WebSocket 和 Http

WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解

+

WebSocket和Http的关系

+

有交集,但是并不是全部。另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。通俗来说,你可以用HTTP协议传输非Html数据,就是这样=。=再简单来说,层级不一样。

+

Websocket是什么样的协议,具体有什么优点

首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。

+

HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么在HTTP1.0中,这次HTTP请求就结束了。

+

在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。

+

但是请记住 Request = Response,在HTTP中永远是这样,也就是说一个request只能有一个response。

+

而且这个response也是被动的,不能主动发起。

+

教练,你BB了这么多,跟Websocket有什么关系呢?

+

_(:з」∠)_好吧,我正准备说Websocket呢。。

+

首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。在握手阶段是一样的

+

——-以下涉及专业技术内容,不想看的可以跳过,或者只看加黑内容——–

+

首先我们来看个典型的Websocket握手(借用Wikipedia的。。)

+
1
2
3
4
5
6
7
8
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
+ +

熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。我会顺便讲解下作用。

+
1
2
Upgrade: websocket
Connection: Upgrade
+ +

这个就是Websocket的核心了,告诉Apache、Nginx等服务器:注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。

+
1
2
3
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
+ +

首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。

+

然后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:今晚我要服务A,别搞错啦~

+

最后,Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。。不过现在还好,已经定下来啦大家都使用的一个东西 脱水:服务员,我要的是13岁的噢→_→

+

然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!

+
1
2
3
4
5
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
+ +

这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~

+
1
2
Upgrade: websocket
Connection: Upgrade
+ +

依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。

+

然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。

+

后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。

+

至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。具体的协议就不在这阐述了。

+

——————技术解析部分完毕——————

+

+

你TMD又BBB了这么久,那到底Websocket有什么鬼用,http long poll,或者ajax轮询不都可以实现实时信息传递么。

+

+

好好好,年轻人,那我们来讲一讲Websocket有什么用。来给你吃点胡(苏)萝(丹)卜(红)

+

+

Websocket的作用

在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。

+

首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

+

场景再现:

+
1
2
3
4
5
6
7
8
9
10
11
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response)
--- loop
+ +

long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。

+

场景再现:

+
1
2
3
4
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
--- loop
+ +

从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。

+

何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。

+

简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。

+

说完这个,我们再来说一说上面的缺陷(原谅我废话这么多吧OAQ)

+

从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。

+

ajax轮询 需要服务器有很快的处理速度和资源。(速度)

+

long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)

+

所以ajax轮询 和long poll 都有可能发生这种情况。

+
1
2
3
4
5
客户端:啦啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)
客户端:。。。好吧,啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)
客户端:。。。
+ +

然后服务端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我错了。。这又是梗。。)

+
+

言归正传,我们来说Websocket吧通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。

+

一种需要更快的速度,一种需要更多的’电话’。这两种都会导致’电话’的需求越来越高。

+

哦对了,忘记说了HTTP还是一个无状态协议。(感谢评论区的各位指出OAQ)

+

通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。

+

所以在这种情况下出现了,Websocket出现了。

+

他解决了HTTP的这几个难题。

+

首先,被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。

+

所以上面的情景可以做如下修改:

+
1
2
3
4
5
6
7
8
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
+ +

就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)

+

这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。

+

那么为什么他会解决服务器上消耗资源的问题呢?

+

其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。

+

简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)。

+

本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢。,导致客服不够。

+

Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员在统一转交给客户。这样就可以解决客服处理速度过慢的问题了。

+

同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输identity info(鉴别信息),来告诉服务端你是谁。

+

虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。

+

但是Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。

+

同时由客户主动询问,转换为服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。),没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的客服(Handler)了

+
+

至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:不能

+

但是可以通过上面说的 long poll 和 ajax 轮询来 模拟出类似的效果

+

祝君无Bug~

+

作者:Ovear

+

链接:点击查看原文

+

来源:知乎

+
+
Author: 淳淳同学
Link: https://leedebug.github.io/2021/03/17/%E7%9C%8B%E5%AE%8C%E8%AE%A9%E4%BD%A0%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82Websocket%E5%8E%9F%E7%90%86/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/03/25/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments/index.html" "b/2021/03/25/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments/index.html" new file mode 100644 index 000000000..c2752156b --- /dev/null +++ "b/2021/03/25/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments/index.html" @@ -0,0 +1,173 @@ +VSCode注释高亮插件 Better Comments | 淳淳同学的个人博客 + + + + + + + + + + + + +

VSCode注释高亮插件 Better Comments

+

本文概要

+
+

安装插件

打开VSCode的插件市场,搜索Better Comments,点击install

+

插件安装

+

使用

该插件提供5中默认的高亮方式,配置信息如下:

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
"better-comments.tags": [
{
"tag": "!",
"color": "#FF2D00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "?",
"color": "#3498DB",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "//",
"color": "#474747",
"strikethrough": true,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "todo",
"color": "#FF8C00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "*",
"color": "#98C379",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
}
]
+ +

效果如下所示:

+

+

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/03/25/VSCode%E6%B3%A8%E9%87%8A%E9%AB%98%E4%BA%AE%E6%8F%92%E4%BB%B6BetterComments/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/04/02/\351\242\204\351\230\262XSS\346\224\273\345\207\273/index.html" "b/2021/04/02/\351\242\204\351\230\262XSS\346\224\273\345\207\273/index.html" new file mode 100644 index 000000000..b37d571bd --- /dev/null +++ "b/2021/04/02/\351\242\204\351\230\262XSS\346\224\273\345\207\273/index.html" @@ -0,0 +1,180 @@ +预防XSS攻击插件 js-xss | 淳淳同学的个人博客 + + + + + + + + + + + + +

预防XSS攻击插件 js-xss

+

在面向客户开发时,特别是根据客户输入的内容进行入库试,无法预计会输入什么,所以需要对客户输入的内容进行过滤,以免引起不必要的bug甚至数据库崩掉

+
+

参考网站

+

安装插件

1
npm install xss
+ +

引入插件

1
import { getDefaultWhiteList, FilterXSS } from 'xss';
+ +

封装函数

插件的github文档中给出了很多api,根据自己的需求进行封装即可,如下所示:

+
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
export const filterXSS = (() => {
const whiteList: any = getDefaultWhiteList(); // 获取默认白名单
// 添加新的白名单标签或属性
for (const i of Object.keys(whiteList)) {
whiteList[i].push('style', 'class');
if (i === 'table' && whiteList[i]) {
whiteList[i].push('cellpadding', 'cellspacing', 'bordercolor');
}
}
whiteList.strike = ['style', 'class'];
const options = {
whiteList,
css: false,
stripIgnoreTag: true, // 非白名单标签,过滤标签,显示标签里的内容
stripIgnoreTagBody: ['script', 'style'],
onTagAttr(tag: any, name: any, value: any, isWhiteAttr: any) {
// 过滤img标签的src属性
if (tag === 'img' && name === 'src') {
return `${name}="data:image/ico;base64,aWNv" data-${name}=${value}`;
}
// 处理a标签
if (tag === 'a') {
return `${name}=${value} style="pointer-events: none;"`;
}
// 如果不返回任何值,表示还是按照默认的方法处理
},
};
const myxss = new FilterXSS(options);
return myxss.process.bind(myxss);
})();
+ +

使用方法

1
2
3
4
import { filterXSS } from '@/utils';

var filterData = filterXSS(res.data);
console.log(filterData);
+ + +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/04/02/%E9%A2%84%E9%98%B2XSS%E6%94%BB%E5%87%BB/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/04/08/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247/index.html" "b/2021/04/08/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247/index.html" new file mode 100644 index 000000000..f86474a69 --- /dev/null +++ "b/2021/04/08/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247/index.html" @@ -0,0 +1,476 @@ +JavaScript运算符优先级 | 淳淳同学的个人博客 + + + + + + + + + + + + +

JavaScript运算符优先级

+

运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

+
+

优先级汇总表

下面的表将所有运算符按照优先级的不同从高(20+)到低(1)排列。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
优先级运算类型关联性运算符
21圆括号n/a(不相关)( … )
20成员访问从左到右… . …
需计算的成员访问从左到右… [ … ]
new (带参数列表)n/anew … ( … )
函数调用从左到右… ( … )
可选链(Optional chaining)从左到右?.
19new (无参数列表)从右到左new …
18后置递增(运算符在后)n/a
+  
… ++
后置递减(运算符在后)… --
17逻辑非从右到左! …
按位非~ …
一元加法+ …
一元减法- …
前置递增++ …
前置递减-- …
typeoftypeof …
voidvoid …
deletedelete …
awaitawait …
16从右到左… ** …
15乘法从左到右
+  
… * …
除法… / …
取模… % …
14加法从左到右
+  
… + …
减法… - …
13按位左移从左到右… << …
按位右移… >> …
无符号右移… >>> …
12小于从左到右… < …
小于等于… <= …
大于… > …
大于等于… >= …
in… in …
instanceof… instanceof …
11等号从左到右
+  
… == …
非等号… != …
全等号… === …
非全等号… !== …
10按位与从左到右… & …
9按位异或从左到右… ^ …
8按位或从左到右… | …
7逻辑与从左到右… && …
6逻辑或从左到右… || …
5空值合并从左到右… ?? …
4条件运算符从右到左… ? … : …
3赋值从右到左… = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
2yield从右到左yield …
yield*yield* …
1展开运算符n/a... …
0逗号从左到右… , …
+ +

示例

1
2
3
4
5
6
7
8
9
A && B
// 若A为true,则return B。若A为false,return A

A || B
// 若A为true,return A。若A为false,则return B

false || 3 && 2
// ||的左侧为false,所以返回(3 && 2)的结果;
// 又,3为true,所以直接返回2
+ +

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/04/08/JavaScript%E8%BF%90%E7%AE%97%E7%AC%A6%E4%BC%98%E5%85%88%E7%BA%A7/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/05/24/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223/index.html" "b/2021/05/24/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223/index.html" new file mode 100644 index 000000000..b6ee7c553 --- /dev/null +++ "b/2021/05/24/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223/index.html" @@ -0,0 +1,396 @@ +2021前端知识点总结 | 淳淳同学的个人博客 + + + + + + + + + + + + +

2021前端知识点总结

+ +

目录

[TOC]

+

JavaScript

    +
  1. +
+
    +
  • +
+
    +
  1. +
+
    +
  • +
+

Vue 2.x

    +
  1. BiliBili: Vue 2.x 源码解读(12) —— path阶段
  2. +
+
    +
  • Emm…,慢慢看
  • +
+
    +
  1. v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?
  2. +
+
    +
  • 源码: compiler/codegen/index.js
  • +
  • Vue 2.x版本中,v-if的优先级大于v-for
  • +
  • Vue 3.x版本中,v-for的优先级大于v-if
  • +
  • render函数: with(this){return _c('div', { ... })}
  • +
+
    +
  1. Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?
  2. +
+
    +
  • 源码: src\core\instance\state.js - initData()
  • +
  • Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的
  • +
  • 采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题
  • +
  • 而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况
  • +
+
    +
  1. 你知道vue中key的作用和工作原理吗?说说你对它的理解。
  2. +
+
    +
  • 源码: src\core\vdom\patch.js - updateChildren()
  • +
  • key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两 个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操 作量,提高性能
  • +
  • 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug(暂时未知)
  • +
  • vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果
  • +
+
    +
  1. 你怎么理解vue中的diff算法?
  2. +
+
    +
  • 源码1: 必要性,lifecycle.js - mountComponent()
      +
    • 组件中可能存在很多个data中的key使用
    • +
    +
  • +
  • 源码2: 执行方式,patch.js - patchVnode()
      +
    • patchVnode是diff发生的地方,整体策略: 深度优先,同层比较
    • +
    +
  • +
  • 源码3: 高效性,patch.js - updateChildren()
  • +
  • diff算法是虚拟DOM技术的必然产物: 通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上
  • +
  • 另外,也需要 diff 高效的执行对比过程,从而降低时间复杂度为O(n)
  • +
  • vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方
  • +
  • vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch
  • +
  • diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做首首、尾尾、首尾、尾首4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。
  • +
+
    +
  1. 谈一谈对vue组件化的理解?
  2. +
+
    +
  • 源码1: 组件定义,src\core\global-api\assets.js
      +
    • vue-loader会编译template为render函数,最终导出的依然是组件配置对象
    • +
    +
  • +
  • 源码2: 组件化优点,lifecycle.js - mountComponent()
      +
    • 组件、Watcher、渲染函数和更新函数之间的关系
    • +
    +
  • +
  • 源码3: 组件化实现: 构造函数,src\core\global-api\extend.js、实例化及挂载,src\core\vdom\patch.js - createElm()
  • +
  • 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
  • +
  • 组件化开发能大幅提高应用开发效率、测试性、复用性等;
  • +
  • 组件使用按分类有: 页面组件、业务组件、通用组件;
  • +
  • vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
  • +
  • vue中常见组件化技术有: 属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
  • +
  • 合理的划分组件,有助于提升应用性能;
  • +
  • 组件应该是高内聚、低耦合的;
  • +
  • 遵循单向数据流的原则。
  • +
+
    +
  1. 谈一谈对vue设计原则的理解?
  2. +
+
    +
  • 在vue的官网上写着大大的定义和特点: 渐进式JavaScript框架、易用、灵活和高效
  • +
+
    +
  1. 谈谈你对MVC、MVP和MVVM的理解?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你了解哪些Vue性能优化方法?
  2. +
+
    +
  • 路由懒加载
  • +
  • keep-alive缓存页面
  • +
  • 使用v-show复用DOM
  • +
  • v-for 遍历避免同时使用 v-if
  • +
  • 长列表性能优化,静态列表:list = Object.freeze([])
  • +
  • 虚拟滚动:vxe-table
  • +
  • 事件的销毁,Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。beforeDestroy() { clearInterval(this.timer) }
  • +
  • 图片懒加载,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域 内的图片先不做加载,等到滚动到可视区域后再去加载。参考项目:vue-lazyload,代码:<img v-lazy="/static/img/1.png">
  • +
  • 第三方插件按需引入,import { Button, Select } from 'element-ui';
  • +
  • 无状态的组件标记为函数式组件,<template functional> ... </template>
  • +
  • 子组件分割,独立可复用功能可抽象出来
  • +
  • 变量本地化,如果有for循环等频繁访问this.xxx的情况,提前赋值给本地变量
  • +
  • SSR
  • +
+
    +
  1. 简单说一说vuex使用及其理解?
  2. +
+
    +
  • Vuex实现了一个单向数据流,在全局拥有一个state存放数据,当组件要更改state中的数据时,必须通过mutation提交修改信息,mutation同时提供了订阅者模式供外部插件调用获取state数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作 需要走action,但action也是无法直接修改state的,还是需要通过mutation来修改state的数据。最后,根据state的变化,渲染到视图上。
  • +
+
    +
  1. vue中组件之间的通信方式?
  2. +
+
    +
  • props ★★
      +
    • 父组件 A 通过 props 向子组件 B 传递值, B 组件传递 A 组件通过 $emit A 组件通过 v-on/@ 触发
    • +
    • 子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
    • +
    +
  • +
  • $emit/$on 事件总线 ★★ +
  • +
  • vuex ★★★
      +
    • 结合localStorage保存登录信息及权限列表等
    • +
    +
  • +
  • $parent/$children
  • +
  • $attrs/$listeners
      +
    • 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法。
    • +
    • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。通常配合 interitAttrs 选项一起使用。
    • +
    • $listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v- on=”$listeners” 传入内部组件
    • +
    +
  • +
  • provide/inject ★★★
      +
    • 优点:使用简单 缺点:不是响应式
    • +
    • 父级:provide: { name: '王者峡谷' //这种绑定是不可响应的 }name: this会有响应式,把当前组件实例传递下去,但子组件会绑定一些多余的属性,比如props、methonds等)
    • +
    • 子级:inject: ['name'] }
    • +
    +
  • +
+
    +
  1. vue-router 中的导航钩子由那些?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 什么是递归组件?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 说一说vue响应式理解?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. vue如果想要扩展某个组件现有组件时怎么做?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. vue为什么要求组件模版只能有一个根元素?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. watch和computed的区别以及怎么选用?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你知道nextTick的原理吗?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 你知道vue双向数据绑定的原理吗?
  2. +
+
    +
  • 源码: compiler
  • +
+
    +
  1. 简单说一说vue生命周期的理解?
  2. +
+
    +
  • 源码: compiler
  • +
+

Vue 3.x

    +
  1. vue3.x的新特性研究
  2. +
+
    +
  • Emm…,慢慢看
  • +
+

Webpack

    +
  1. +
+
    +
  • +
+

Css & Css3

    +
  1. +
+
    +
  • +
+

项目结构化?

    +
  1. +
+
    +
  • +
+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/05/24/2021%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/05/27/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel/index.html" "b/2021/05/27/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel/index.html" new file mode 100644 index 000000000..947744a2e --- /dev/null +++ "b/2021/05/27/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel/index.html" @@ -0,0 +1,346 @@ +【个人开源】Vue3 轮播组件: v3-carousel | 淳淳同学的个人博客 + + + + + + + + + + + + +

【个人开源】Vue3 轮播组件: v3-carousel

+

引水方知开源不易

+

与朋友首次开源了一个轮播插件,希望大家积极品尝

+
+

介绍

基于 vue3 composition api 编写的轮播插件,多种属性适配,轮播内容 可完全自定义,基本可以满足大部分的轮播需求。

+

基本功能介绍:

+
    +
  • 是否开启自动轮播,自定义轮播时间
  • +
  • 鼠标移入后暂停轮播,鼠标移出后重置轮播
  • +
  • 点击 左侧/右侧 切换按钮,手动切换
  • +
  • 点击 底部轮播指示器,手动切换
  • +
  • 切换按钮 与 轮播指示器,可设置 hover 展示
  • +
  • 左侧切换向左滚动,右侧切换向右滚动
  • +
  • +
+

在线Demo:正在制作…

+

安装

1
npm install v3-carousel
+

or

+
1
yarn add v3-carousel
+

使用

main.js

+
1
2
3
4
5
6
import { createApp } from "vue";
import App from "./App.vue";
import Carousel from "v3-carousel"; // 引入

const app = createApp(App)
app.use(Carousel).mount('#app') // 使用
+ +
+

注意点:将你需要显示的图片使用CarouselItem包裹起来(创建CarouselItem暂时必须使用v-for循环完成,因为我需要使用到idx来操作),完成之后你还需要将这些CarouselItem用一个大的Carousel包裹起来,再给Carousel添加你需要的属性,好了,到这里一个实例就完成了,你可以去网页上查看轮播图了

+
+

App.vue

+
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template>
<div class="app">
<Carousel
:autoplay="true"
:duration="2000"
:initIndex="2"
:direction="true"
directionMode="hover"
:directionSize="20"
directionColor="skyblue"
:indicator="true"
indicatorMode="always"
indicatorColor="white"
indicatorActiveColor="skyblue"
@before-moving="beforeMoving"
@after-moving="afterMoving"
>
<CarouselItem v-for="(item, index) in data" :key="index" :idx="index">
<img :src="item" />
</CarouselItem>
</Carousel>
</div>
</template>

<script>
import { defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
name: "App",
setup() {
const state = reactive({
data: [
"https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00693-2745.jpg",
"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2190440116,1436403087&fm=26&gp=0.jpg",
"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3593656793,3600757928&fm=26&gp=0.jpg",
],
});

function beforeMoving(dire) {
console.log("before", dire);
},
function afterMoving(obj) {
console.log("after", obj);
},

return {
...toRefs(state),
beforeMoving,
afterMoving,
};
},
});
</script>
+ + +

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
property nametypedefault valuemeaning
containerWidthString100%设置整个carousel容器的宽度,当然你也可以使用vw,rem,em等像素单位
containerHeightString100%设置整个carousel容器的g高度,同上
durationNumber3000轮播间隔是多久一次
initIndexNumber0初始化显示的图片索引
autoplayBooleantrue是否自动开始轮播
directionBooleantrue是否需要切换按钮(即 上、下一张按钮)
directionModeStringalways切换按钮的展示方式,可选 always、hover
directionColorStringwhite切换按钮的颜色
directionSizeNumber25切换按钮的大小,单位(px)
indicatorBooleantrue是否需要轮播图指示器(底部当前选中标识)
indicatorModeStringalways切换按钮的展示方式,可选 always、hover
indicatorColorString#FFFFFF80未选中时的指示器颜色
indicatorActiveColorString#FFFFFF选中时的指示器颜色
+ + + + + + + + + + + + + + + + + + +
event Nameparmasmeaning
@before-moving该钩子函数拥有一个对象参数,你可以获取到它们:轮播的方向(direction)以及当前轮播的索引(index)视图移动前会执行的钩子函数,如果您想在轮播图轮播前做一些逻辑可以使用该钩子
@after-moving同上…视图移动完成后会执行的钩子函数,如果您想在轮播图轮播完成之后做一些逻辑可以使用该钩子
+

CarouselItem 组件选项(Props)

+ + + + + + + + + + + + + + +
property nametypedefault valuemeaning
idxNumber0每个子实例对应的索引,一般用 v-for 中的第二个参数即可
+

联系方式

+

使用如果出现问题欢迎来讨论,觉得好用的话就点个 star 吧,o( ̄▽ ̄)o

+

有什么建议欢迎大佬们提交 pr,谢谢!

+
+

repo归属者

    +
  • WeChat: x972761675
  • +
  • 前端qq交流群: 700785102
  • +
+

目前维护者: 淳淳同学

+

相关链接

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/05/27/Vue3%E8%BD%AE%E6%92%AD%E7%BB%84%E4%BB%B6-v3-carousel/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
\ No newline at end of file diff --git "a/2021/07/05/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211/index.html" "b/2021/07/05/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211/index.html" new file mode 100644 index 000000000..048f24b06 --- /dev/null +++ "b/2021/07/05/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211/index.html" @@ -0,0 +1,584 @@ +Vue源码解读(知识点总结) | 淳淳同学的个人博客 + + + + + + + + + + + + +

Vue源码解读(知识点总结)

+

为了方便自己对知识点的巩固和理解,整理了李永宁大佬 12 篇《Vue源码解读》的文末知识点总结,在这里可以一览天下。如果想看详细文章,可点击标题下方的“阅读原文”即可。

+
+

(1)前言

阅读原文

+

(2)Vue 初始化过程

阅读原文

+

Vue 的初始化过程(new Vue(options))都做了什么?

    +
  • 处理组件配置项
      +
    • 初始化根组件时进行了选项合并操作,将全局配置合并到根组件的局部配置上
    • +
    • 初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以提高代码的执行效率
    • +
    +
  • +
  • 初始化组件实例的关系属性,比如 $parent、$children、$root、$refs 等
  • +
  • 处理自定义事件
  • +
  • 调用 beforeCreate 钩子函数
  • +
  • 初始化组件的 inject 配置项,得到 ret[key] = val 形式的配置对象,然后对该配置对象进行响应式处理,并代理每个 key 到 vm 实例上
  • +
  • 数据响应式,处理 props、methods、data、computed、watch 等选项
  • +
  • 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
  • +
  • 调用 created 钩子函数
  • +
  • 如果发现配置项上有 el 选项,则自动调用 $mount 方法,也就是说有了 el 选项,就不需要再手动调用 $mount 方法,反之,没提供 el 选项则必须调用 $mount
  • +
  • 接下来则进入挂载阶段
  • +
+

(3)响应式原理

阅读原文

+

Vue 响应式原理是怎么实现的?

    +
  • 响应式的核心是通过 Object.defineProperty 拦截对数据的访问和设置
  • +
  • 响应式的数据分为两类:
      +
    • 对象,循环遍历对象的所有属性,为每个属性设置 getter、setter,以达到拦截访问和设置的目的,如果属性值依旧为对象,则递归为属性值上的每个 key 设置 getter、setter
        +
      • 访问数据时(obj.key)进行依赖收集,在 dep 中存储相关的 watcher
      • +
      • 设置数据时由 dep 通知相关的 watcher 去更新
      • +
      +
    • +
    • 数组,增强数组的那 7 个可以更改自身的原型方法,然后拦截对这些方法的操作
        +
      • 添加新数据时进行响应式处理,然后由 dep 通知 watcher 去更新
      • +
      • 删除数据时,也要由 dep 通知 watcher 去更新
      • +
      +
    • +
    +
  • +
+

methods、computed 和 watch 有什么区别?

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="en">

<head>
<title>methods、computed、watch 有什么区别</title>
</head>

<body>
<div id="app">
<!-- methods -->
<div>{{ returnMsg() }}</div>
<div>{{ returnMsg() }}</div>
<!-- computed -->
<div>{{ getMsg }}</div>
<div>{{ getMsg }}</div>
</div>
<script src="../../dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 'test'
},
mounted() {
setTimeout(() => {
this.msg = 'msg is changed'
}, 1000)
},
methods: {
returnMsg() {
console.log('methods: returnMsg')
return this.msg
}
},
computed: {
getMsg() {
console.log('computed: getMsg')
return this.msg + ' hello computed'
}
},
watch: {
msg: function(val, oldVal) {
console.log('watch: msg')
new Promise(resolve => {
setTimeout(() => {
this.msg = 'msg is changed by watch'
}, 1000)
})
}
}
})
</script>
</body>

</html>
+ +

methodsComputedWatch.gif

+

示例其实就是答案了

+
    +
  • 使用场景
      +
    • methods 一般用于封装一些较为复杂的处理逻辑(同步、异步)
    • +
    • computed 一般用于封装一些简单的同步逻辑,将经过处理的数据返回,然后显示在模版中,以减轻模版的重量
    • +
    • watch 一般用于当需要在数据变化时执行异步或开销较大的操作
    • +
    +
  • +
  • 区别
      +
    • methods VS computed
        +
      • 通过示例会发现,如果在一次渲染中,有多个地方使用了同一个 methods 或 computed 属性,methods 会被执行多次,而 computed 的回调函数则只会被执行一次。
      • +
      • 通过阅读源码我们知道,在一次渲染中,多次访问 computedProperty,只会在第一次执行 computed 属性的回调函数,后续的其它访问,则直接使用第一次的执行结果(watcher.value),而这一切的实现原理则是通过对 watcher.dirty 属性的控制实现的。而 methods,每一次的访问则是简单的方法调用(this.xxMethods)。
      • +
      +
    • +
    • computed VS watch
        +
      • 通过阅读源码我们知道,computed 和 watch 的本质是一样的,内部都是通过 Watcher 来实现的,其实没什么区别,非要说区别的化就两点:1、使用场景上的区别,2、computed 默认是懒执行的,切不可更改。
      • +
      +
    • +
    • methods VS watch
        +
      • methods 和 watch 之间其实没什么可比的,完全是两个东西,不过在使用上可以把 watch 中一些逻辑抽到 methods 中,提高代码的可读性。
      • +
      +
    • +
    +
  • +
+

(4)异步更新

阅读原文

+

Vue 的异步更新机制是如何实现的?

    +
  • Vue 的异步更新机制的核心是利用了浏览器的异步任务队列来实现的,首选微任务队列,宏任务队列次之。
  • +
  • 当响应式数据更新后,会调用 dep.notify 方法,通知 dep 中收集的 watcher 去执行 update 方法,watcher.update 将 watcher 自己放入一个 watcher 队列(全局的 queue 数组)。
  • +
  • 然后通过 nextTick 方法将一个刷新 watcher 队列的方法(flushSchedulerQueue)放入一个全局的 callbacks 数组中。
  • +
  • 如果此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数,则执行 timerFunc 函数,将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数,等待其执行完成以后再放入下一个 flushCallbacks 函数。
  • +
  • flushCallbacks 函数负责执行 callbacks 数组中的所有 flushSchedulerQueue 函数。
  • +
  • flushSchedulerQueue 函数负责刷新 watcher 队列,即执行 queue 数组中每一个 watcher 的 run 方法,从而进入更新阶段,比如执行组件更新函数或者执行用户 watch 的回调函数。
  • +
  • 完整的执行过程其实就是今天源码阅读的过程。
  • +
+

Vue 的 nextTick API 是如何实现的?

Vue.nextTick 或者 vm.$nextTick 的原理其实很简单,就做了两件事:

+
    +
  • 将传递的回调函数用 try catch 包裹然后放入 callbacks 数组
  • +
  • 执行 timerFunc 函数,在浏览器的异步任务队列放入一个刷新 callbacks 数组的函数
  • +
+

(5)全局 API

阅读原文

+

Vue.use(plugin) 做了什么?

负责安装 plugin 插件,其实就是执行插件提供的 install 方法。

+
    +
  • 首先判断该插件是否已经安装过
  • +
  • 如果没有,则执行插件提供的 install 方法安装插件,具体做什么有插件自己决定
  • +
+

Vue.mixin(options) 做了什么?

负责在 Vue 的全局配置上合并 options 配置。然后在每个组件生成 vnode 时会将全局配置合并到组件自身的配置上来。

+
    +
  • 标准化 options 对象上的 props、inject、directive 选项的格式
  • +
  • 处理 options 上的 extends 和 mixins,分别将他们合并到全局配置上
  • +
  • 然后将 options 配置和全局配置进行合并,选项冲突时 options 配置会覆盖全局配置
  • +
+

Vue.component(compName, Comp) 做了什么?

负责注册全局组件。其实就是将组件配置注册到全局配置的 components 选项上(options.components),然后各个子组件在生成 vnode 时会将全局的 components 选项合并到局部的 components 配置项上。

+
    +
  • 如果第二个参数为空,则表示获取 compName 的组件构造函数
  • +
  • 如果 Comp 是组件配置对象,则使用 Vue.extend 方法得到组件构造函数,否则直接进行下一步
  • +
  • 在全局配置上设置组件信息,this.options.components.compName = CompConstructor
  • +
+

Vue.directive(‘my-directive’, {xx}) 做了什么?

在全局注册 my-directive 指令,然后每个子组件在生成 vnode 时会将全局的 directives 选项合并到局部的 directives 选项中。原理同 Vue.component 方法:

+
    +
  • 如果第二个参数为空,则获取指定指令的配置对象
  • +
  • 如果不为空,如果第二个参数是一个函数的话,则生成配置对象 { bind: 第二个参数, update: 第二个参数 }
  • +
  • 然后将指令配置对象设置到全局配置上,this.options.directives[‘my-directive’] = {xx}
  • +
+

Vue.filter(‘my-filter’, function(val) {xx}) 做了什么?

负责在全局注册过滤器 my-filter,然后每个子组件在生成 vnode 时会将全局的 filters 选项合并到局部的 filters 选项中。原理是:

+
    +
  • 如果没有提供第二个参数,则获取 my-filter 过滤器的回调函数
  • +
  • 如果提供了第二个参数,则是设置 this.options.filters[‘my-filter’] = function(val) {xx}。
  • +
+

Vue.extend(options) 做了什么?

Vue.extend 基于 Vue 创建一个子类,参数 options 会作为该子类的默认全局配置,就像 Vue 的默认全局配置一样。所以通过 Vue.extend 扩展一个子类,一大用处就是内置一些公共配置,供子类的子类使用。

+
    +
  • 定义子类构造函数,这里和 Vue 一样,也是调用 _init(options)
  • +
  • 合并 Vue 的配置和 options,如果选项冲突,则 options 的选项会覆盖 Vue 的配置项
  • +
  • 给子类定义全局 API,值为 Vue 的全局 API,比如 Sub.extend = Super.extend,这样子类同样可以扩展出其它子类
  • +
  • 返回子类 Sub
  • +
+

Vue.set(target, key, val) 做了什么?

由于 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = ‘hi’),所以通过 Vue.set 为向响应式对象中添加一个 property,可以确保这个新 property 同样是响应式的,且触发视图更新。

+
    +
  • 更新数组指定下标的元素:Vue.set(array, idx, val),内部通过 splice 方法实现响应式更新
  • +
  • 更新对象已有属性:Vue.set(obj, key ,val),直接更新即可 => obj[key] = val
  • +
  • 不能向 Vue 实例或者 $data 动态添加根级别的响应式数据
  • +
  • Vue.set(obj, key, val),如果 obj 不是响应式对象,会执行 obj[key] = val,但是不会做响应式处理
  • +
  • Vue.set(obj, key, val),为响应式对象 obj 增加一个新的 key,则通过 defineReactive 方法设置响应式,并触发依赖更新
  • +
+

面试官 问:Vue.delete(target, key) 做了什么?

删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,但是你应该很少会使用它。当然同样不能删除根级别的响应式属性。

+
    +
  • Vue.delete(array, idx),删除指定下标的元素,内部是通过 splice 方法实现的
  • +
  • 删除响应式对象上的某个属性:Vue.delete(obj, key),内部是执行 delete obj.key,然后执行依赖更新即可
  • +
+

Vue.nextTick(cb) 做了什么?

Vue.nextTick(cb) 方法的作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想立即获取更改过后的 DOM 数据:

+
1
2
3
4
5
this.key = 'new val'

Vue.nextTick(function() {
// DOM 更新了
})
+ +

其内部的执行过程是:

+
    +
  • this.key = ‘new val,触发依赖通知更新,将负责更新的 watcher 放入 watcher 队列
  • +
  • 将刷新 watcher 队列的函数放到 callbacks 数组中
  • +
  • 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数
  • +
  • Vue.nextTick(cb) 来插队,将 cb 函数放入 callbacks 数组
  • +
  • 待将来的某个时刻执行刷新 callbacks 数组的函数
  • +
  • 然后执行 callbacks 数组中的众多函数,触发 watcher.run 的执行,更新 DOM
  • +
  • 由于 cb 函数是在后面放到 callbacks 数组,所以这就保证了先完成的 DOM 更新,再执行 cb 函数
  • +
+

(6)实例方法

阅读原文

+

面试官 问:vm.$set(obj, key, val) 做了什么?

vm.$set 用于向响应式对象添加一个新的 property,并确保这个新的 property 同样是响应式的,并触发视图更新。由于 Vue 无法探测对象新增属性或者通过索引为数组新增一个元素,比如:this.obj.newProperty = 'val'this.arr[3] = 'val'。所以这才有了 vm.$set,它是 Vue.set 的别名。

+
    +
  • 为对象添加一个新的响应式数据:调用 defineReactive 方法为对象增加响应式数据,然后执行 dep.notify 进行依赖通知,更新视图
  • +
  • 为数组添加一个新的响应式数据:通过 splice 方法实现
  • +
+

vm.$delete(obj, key) 做了什么?

vm.$delete 用于删除对象上的属性。如果对象是响应式的,且能确保能触发视图更新。该方法主要用于避开 Vue 不能检测属性被删除的情况。它是 Vue.delete 的别名。

+
    +
  • 删除数组指定下标的元素,内部通过 splice 方法来完成
  • +
  • 删除对象上的指定属性,则是先通过 delete 运算符删除该属性,然后执行 dep.notify 进行依赖通知,更新视图
  • +
+

vm.$watch(expOrFn, callback, [options]) 做了什么?

vm.$watch 负责观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。当其发生变化时,回调函数就会被执行,并为回调函数传递两个参数,第一个为更新后的新值,第二个为老值。

+

这里需要 注意 一点的是:如果观察的是一个对象,比如:数组,当你用数组方法,比如 push 为数组新增一个元素时,回调函数被触发时传递的新值和老值相同,因为它们指向同一个引用,所以在观察一个对象并且在回调函数中有新老值是否相等的判断时需要注意。

+

vm.$watch 的第一个参数只接收简单的响应式数据的键路径,对于更复杂的表达式建议使用函数作为第一个参数。

+

至于 vm.$watch 的内部原理是:

+
    +
  • 设置 options.user = true,标志是一个用户 watcher
  • +
  • 实例化一个 Watcher 实例,当检测到数据更新时,通过 watcher 去触发回调函数的执行,并传递新老值作为回调函数的参数
  • +
  • 返回一个 unwatch 函数,用于取消观察
  • +
+

vm.$on(event, callback) 做了什么?

监听当前实例上的自定义事件,事件可由 vm.$emit 触发,回调函数会接收所有传入事件触发函数vm.$emit的额外参数。

+

vm.$on 的原理很简单,就是处理传递的 event 和 callback 两个参数,将注册的事件和回调函数以键值对的形式存储到 vm._event 对象中,vm._events = { eventName: [cb1, cb2, ...], ... }

+

vm.$emit(eventName, […args]) 做了什么?

触发当前实例上的指定事件,附加参数都会传递给事件的回调函数。

+

其内部原理就是执行 vm._events[eventName] 中所有的回调函数。

+
+

备注:从 on和on 和 on和emit 的实现原理也能看出,组件的自定义事件其实是谁触发谁监听,所以在这会儿再回头看 Vue 源码解读(2)—— Vue 初始化过程 中关于 initEvent 的解释就会明白在说什么,因为组件自定义事件的处理内部用的就是 vm.on、vm.on、vm.on、vm.emit。

+
+

vm.$off([event, callback]) 做了什么?

移除自定义事件监听器,即移除 vm._events 对象上相关数据。

+
    +
  • 如果没有提供参数,则移除实例的所有事件监听
  • +
  • 如果只提供了 event 参数,则移除实例上该事件的所有监听器
  • +
  • 如果两个参数都提供了,则移除实例上该事件对应的监听器
  • +
+

vm.$once(event, callback) 做了什么?

监听一个自定义事件,但是该事件只会被触发一次。一旦触发以后监听器就会被移除。

+

其内部的实现原理是:

+
    +
  • 包装用户传递的回调函数,当包装函数执行的时候,除了会执行用户回调函数之外还会执行 vm.$off(event, 包装函数) 移除该事件
  • +
  • 用 vm.$on(event, 包装函数) 注册事件
  • +
+

vm._update(vnode, hydrating) 做了什么?

官方文档没有说明该 API,这是一个用于源码内部的实例方法,负责更新页面,是页面渲染的入口,其内部根据是否存在 prevVnode 来决定是首次渲染,还是页面更新,从而在调用 patch 函数时传递不同的参数。该方法在业务开发中不会用到。

+

vm.$forceUpdate() 做了什么?

迫使 Vue 实例重新渲染,它仅仅影响组件实例本身和插入插槽内容的子组件,而不是所有子组件。其内部原理到也简单,就是直接调用 vm._watcher.update(),它就是 watcher.update() 方法,执行该方法触发组件更新。

+

vm.$destroy() 做了什么?

负责完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令和事件监听器。在执行过程中会调用 beforeDestroy 和 destroy 两个钩子函数。在大多数业务开发场景下用不到该方法,一般都通过 v-if 指令来操作。其内部原理是:

+
    +
  • 调用 beforeDestroy 钩子函数
  • +
  • 将自己从老爹肚子里($parent)移除,从而销毁和老爹的关系
  • +
  • 通过 watcher.teardown() 来移除依赖监听
  • +
  • 通过 vm._ _ patch _ _(vnode, null) 方法来销毁节点
  • +
  • 调用 destroyed 钩子函数
  • +
  • 通过 vm.$off 方法移除所有的事件监听
  • +
+

vm.$nextTick(cb) 做了什么?

vm.$nextTick 是 Vue.nextTick 的别名,其作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想立即获取更改过后的 DOM 数据:

+
1
2
3
4
5
this.key = 'new val'

Vue.nextTick(function() {
// DOM 更新了
})
+ +

其内部的执行过程是:

+
    +
  • this.key = ‘new val’,触发依赖通知更新,将负责更新的 watcher 放入 watcher 队列
  • +
  • 将刷新 watcher 队列的函数放到 callbacks 数组中
  • +
  • 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数
  • +
  • vm.$nextTick(cb) 来插队,直接将 cb 函数放入 callbacks 数组
  • +
  • 待将来的某个时刻执行刷新 callbacks 数组的函数
  • +
  • 然后执行 callbacks 数组中的众多函数,触发 watcher.run 的执行,更新 DOM
  • +
  • 由于 cb 函数是在后面放到 callbacks 数组,所以这就保证了先完成的 DOM 更新,再执行 cb 函数
  • +
+

vm._render 做了什么?

官方文档没有提供该方法,它是一个用于源码内部的实例方法,负责生成 vnode。其关键代码就一行,执行 render 函数生成 vnode。不过其中加了大量的异常处理代码。

+

(7)Hook Event

阅读原文

+

什么是 Hook Event?

Hook Event 是 Vue 的自定义事件结合生命周期钩子实现的一种从组件外部为组件注入额外生命周期方法的功能。

+

Hook Event 是如果实现的?

1
<comp @hook:lifecycleMethod="method" />
+ +
    +
  • 处理组件自定义事件的时候(vm.$on) 如果发现组件有 hook:xx 格式的事件(xx 为 Vue 的生命周期函数),则将 vm._hasHookEvent 置为 true,表示该组件有 Hook Event

    +
  • +
  • 在组件生命周期方法被触发的时候,内部会通过 callHook 方法来执行这些生命周期函数,在生命周期函数执行之后,如果发现 vm._hasHookEvent 为 true,则表示当前组件有 Hook Event,通过 vm.$emit(‘hook:xx’) 触发 Hook Event 的执行

    +
  • +
+

(8)编译器 之 解析

阅读原文(上篇)

+

阅读原文(下篇)

+

面试官 问:简单说一下 Vue 的编译器都做了什么?

Vue 的编译器做了三件事情:

+
    +
  • 将组件的 html 模版解析成 AST 对象
  • +
  • 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数
  • +
  • 从 AST 生成运行时的渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数
  • +
+

详细说一说编译器的解析过程,它是怎么将 html 字符串模版变成 AST 对象的?

    +
  • 遍历 HTML 模版字符串,通过正则表达式匹配 “<”
  • +
  • 跳过某些不需要处理的标签,比如:注释标签、条件注释标签、Doctype。
      +
    • 备注:整个解析过程的核心是处理开始标签和结束标签
    • +
    +
  • +
  • 解析开始标签
      +
    • 得到一个对象,包括 标签名(tagName)、所有的属性(attrs)、标签在 html 模版字符串中的索引位置
    • +
    • 进一步处理上一步得到的 attrs 属性,将其变成 [{ name: attrName, value: attrVal, start: xx, end: xx }, …] 的形式
    • +
    • 通过标签名、属性对象和当前元素的父元素生成 AST 对象,其实就是一个 普通的 JS 对象,通过 key、value 的形式记录了该元素的一些信息
    • +
    • 接下来进一步处理开始标签上的一些指令,比如 v-pre、v-for、v-if、v-once,并将处理结果放到 AST 对象上
    • +
    • 处理结束将 ast 对象存放到 stack 数组
    • +
    • 处理完成后会截断 html 字符串,将已经处理掉的字符串截掉
    • +
    +
  • +
  • 解析闭合标签
      +
    • 如果匹配到结束标签,就从 stack 数组中拿出最后一个元素,它和当前匹配到的结束标签是一对。
    • +
    • 再次处理开始标签上的属性,这些属性和前面处理的不一样,比如:key、ref、scopedSlot、样式等,并将处理结果放到元素的 AST 对象上
        +
      • 备注:视频中说这块儿有误,回头看了下,没有问题,不需要改,确实是这样
      • +
      +
    • +
    • 然后将当前元素和父元素产生联系,给当前元素的 ast 对象设置 parent 属性,然后将自己放到父元素的 ast 对象的 children 数组中
    • +
    +
  • +
  • 最后遍历完整个 html 模版字符串以后,返回 ast 对象
  • +
+

(9)编译器 之 优化

阅读原文

+

简单说一下 Vue 的编译器都做了什么?

Vue 的编译器做了三件事情:

+
    +
  • 将组件的 html 模版解析成 AST 对象
  • +
  • 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数
  • +
  • 从 AST 生成运行渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数
  • +
+

详细说一下静态标记的过程

    +
  • 标记静态节点
      +
    • 通过递归的方式标记所有的元素节点
    • +
    • 如果节点本身是静态节点,但是存在非静态的子节点,则将节点修改为非静态节点
    • +
    +
  • +
  • 标记静态根节点,基于静态节点,进一步标记静态根节点
      +
    • 如果节点本身是静态节点 && 而且有子节点 && 子节点不全是文本节点,则标记为静态根节点
    • +
    • 如果节点本身不是静态根节点,则递归的遍历所有子节点,在子节点中标记静态根
    • +
    +
  • +
+

什么样的节点才可以被标记为静态节点?

    +
  • 文本节点
  • +
  • 节点上没有 v-bind、v-for、v-if 等指令
  • +
  • 非组件
  • +
+

(10)编译器 之 生成渲染函数

阅读原文

+

简单说一下 Vue 的编译器都做了什么?

Vue 的编译器做了三件事情:

+
    +
  • 将组件的 html 模版解析成 AST 对象
  • +
  • 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数
  • +
  • 从 AST 生成运行渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数
  • +
+

详细说一下渲染函数的生成过程

大家一说到渲染函数,基本上说的就是 render 函数,其实编译器生成的渲染有两类:

+
    +
  • 第一类就是一个 render 函数,负责生成动态节点的 vnode
  • +
  • 第二类是放在一个叫 staticRenderFns 数组中的静态渲染函数,这些函数负责生成静态节点的 vnode
    渲染函数生成的过程,其实就是在遍历 AST 节点,通过递归的方式,处理每个节点,最后生成形如:_c(tag, attr, children, normalizationType) 的结果。tag 是标签名,attr 是属性对象,children 是子节点组成的数组,其中每个元素的格式都是 _c(tag, attr, children, normalizationTYpe) 的形式,normalization 表示节点的规范化类型,是一个数字 0、1、2,不重要。
  • +
+

在处理 AST 节点过程中需要大家重点关注也是面试中常见的问题有:

+
    +
  • 静态节点是怎么处理的(静态节点的处理分为两步)?
      +
    • 将生成静态节点 vnode 函数放到 staticRenderFns 数组中
    • +
    • 返回一个 _m(idx) 的可执行函数,意思是执行 staticRenderFns 数组中下标为 idx 的函数,生成静态节点的 vnode
    • +
    +
  • +
  • v-once、v-if、v-for、组件 等都是怎么处理的?
      +
    • 单纯的 v-once 节点处理方式和静态节点一致
    • +
    • v-if 节点的处理结果是一个三元表达式
    • +
    • v-for 节点的处理结果是可执行的 _l 函数,该函数负责生成 v-for 节点的 vnode
    • +
    • 组件的处理结果和普通元素一样,得到的是形如 _c(compName) 的可执行代码,生成组件的 vnode
    • +
    +
  • +
+

碎碎念

到这里,Vue 编译器 的源码解读就结束了。相信大家在阅读的过程中不免会产生云里雾里的感觉。这个没什么,编译器这块儿确实是比较复杂,可以说是整个框架最难理解也是代码量最大的一部分了。一定要静下心来多读几遍,遇到无法理解的地方,一定要勤动手,通过示例代码加断点调试的方式帮助自己理解。

+

当你读完几遍以后,这时候情况可能就会好一些,但是有些地方可能还会有些晕,这没事,正常。毕竟这是一个框架的编译器,要处理的东西太多太多了,你只需要理解其核心思想(模版解析、静态标记、代码生成)就可以了。后面会有 手写 Vue 系列,编译器这部分会有一个简版的实现,帮助加深对这部分知识的理解。

+

编译器读完以后,会发现有个不明白的地方:编译器最后生成的代码都是经过 with 包裹的,比如:

+
1
2
3
<div id="app">
<div v-for="item in arr" :key="item">{{ item }}</div>
</div>
+ +

经过编译后生成:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
with (this) {
return _c(
'div',
{
attrs:
{
"id": "app"
}
},
_l(
(arr),
function (item) {
return _c(
'div',
{
key: item
},
[_v(_s(item))]
)
}
),
0
)
}
+ +

都知道,with 语句可以扩展作用域链,所以生成的代码中的 _c_l_v_s 都是 this 上一些方法,也就是说在运行时执行这些方法可以生成各个节点的 vnode。

+

所以联系前面的知识,响应式数据更新的整个执行过程就是:

+
    +
  • 响应式拦截到数据的更新
  • +
  • dep 通知 watcher 进行异步更新
  • +
  • watcher 更新时执行组件更新函数 updateComponent
  • +
  • 首先执行 vm._render 生成组件的 vnode,这时就会执行编译器生成的函数
  • +
+

问题:

+
    +
  • 渲染函数中的 _c、_l、、_v、_s 等方法是什么?
  • +
  • 它们是如何生成 vnode 的?
  • +
+

下一篇文章 Vue 源码解读(11)—— render helper 将会带来这部分知识的详细解读,也是面试经常被问题的:比如:v-for 的原理是什么?

+

(11)render helper

阅读原文

+

一个组件是如何变成 VNode?

    +
  • 组件实例初始化,最后执行 $mount 进入挂载阶段
  • +
  • 如果是只包含运行时的 vue.js,只直接进入挂载阶段,因为这时候的组件已经变成了渲染函数,编译过程通过模块打包器 + vue-loader + vue-template-compiler 完成的
  • +
  • 如果没有使用预编译,则必须使用全量的 vue.js
  • +
  • 挂载时如果发现组件配置项上没有 render 选项,则进入编译阶段
  • +
  • 将模版字符串编译成 AST 语法树,其实就是一个普通的 JS 对象
  • +
  • 然后优化 AST,遍历 AST 对象,标记每一个节点是否为静态静态;然后再进一步标记出静态根节点,在组件后续更新时会跳过这些静态节点的更新,以提高性能
  • +
  • 接下来从 AST 生成渲染函数,生成的渲染函数有两部分组成:
      +
    • 负责生成动态节点 VNode 的 render 函数
    • +
    • 还有一个 staticRenderFns 数组,里面每一个元素都是一个生成静态节点 VNode 的函数,这些函数会作为 render 函数的组成部分,负责生成静态节点的 VNode
    • +
    +
  • +
  • 接下来将渲染函数放到组件的配置对象上,进入挂载阶段,即执行 mountComponent 方法
  • +
  • 最终负责渲染组件和更新组件的是一个叫 updateComponent 方法,该方法每次执行前首先需要执行 vm._render 函数,该函数负责执行编译器生成的 render,得到组件的 VNode
  • +
  • 将一个组件生成 VNode 的具体工作是由 render 函数中的 _c_o_l_m 等方法完成的,这些方法都被挂载到 Vue 实例上面,负责在运行时生成组件 VNode
  • +
+
+

提示:到这里首先要明白什么是 VNode,一句话描述就是 —— 组件模版的 JS 对象表现形式,它就是一个普通的 JS 对象,详细描述了组件中各节点的信息

+
+
+

下面说的有点多,其实记住一句就可以了,设置组件配置信息,然后通过 new VNode(组件信息) 生成组件的 VNode

+
+
    +
  • _c,负责生成组件或 HTML 元素的 VNode,_c 是所有 render helper 方法中最复杂,也是最核心的一个方法,其它的 _xx 都是它的组成部分
      +
    • 接收标签、属性 JSON 字符串、子节点数组、节点规范化类型作为参数
    • +
    • 如果标签是平台保留标签或者一个未知的元素,则直接 new VNode(标签信息) 得到 VNode
    • +
    • 如果标签是一个组件,则执行 createComponent 方法生成 VNode
        +
      • 函数式组件执行自己的 render 函数生成 VNode
      • +
      • 普通组件则实例化一个 VNode,并且在在 data.hook 对象上设置 4 个方法,在组件的 patch 阶段会被调用,从而进入子组件的实例化、挂载阶段,然后进行编译生成渲染函数,直至完成渲染
      • +
      • 当然生成 VNode 之前会进行一些配置处理比如:
          +
        • 子组件选项合并,合并全局配置项到组件配置项上
        • +
        • 处理自定义组件的 v-model
        • +
        • 处理组件的 props,提取组件的 props 数据,以组件的 props 配置中的属性为 key,父组件中对应的数据为 value 生成一个 propsData 对象;当组件更新时生成新的 VNode,又会进行这一步,这就是 props 响应式的原理
        • +
        • 处理其它数据,比如监听器
        • +
        • 安装内置的 init、prepatch、insert、destroy 钩子到 data.hooks 对象上,组件 patch 阶段会用到这些钩子方法
        • +
        +
      • +
      +
    • +
    +
  • +
  • _l,运行时渲染 v-for 列表的帮助函数,循环遍历 val 值,依次为每一项执行 render 方法生成 VNode,最终返回一个 VNode 数组
  • +
  • _m,负责生成静态节点的 VNode,即执行 staticRenderFns 数组中指定下标的函数
  • +
+

简单总结 render helper 的作用就是:在 Vue 实例上挂载一些运行时的工具方法,这些方法用在编译器生成的渲染函数中,用于生成组件的 VNode。

+

好了,到这里,一个组件从初始化开始到最终怎么变成 VNode 就讲完了,最后剩下的就是 patch 阶段了,下一篇文章将讲述如何将组件的 VNode 渲染到页面上。

+

(12)patch

阅读原文

+

你能说一说 Vue 的 patch 算法吗?

Vue 的 patch 算法有三个作用:负责首次渲染和后续更新或者销毁组件

+
    +
  • 如果老的 VNode 是真实元素,则表示首次渲染,创建整棵 DOM 树,并插入 body,然后移除老的模版节点
  • +
  • 如果老的 VNode 不是真实元素,并且新的 VNode 也存在,则表示更新阶段,执行 patchVnode
      +
    • 首先是全量更新所有的属性
    • +
    • 如果新老 VNode 都有孩子,则递归执行 updateChildren,进行 diff 过程
        +
      • 针对前端操作 DOM 节点的特点进行如下优化:
      • +
      • 同层比较(降低时间复杂度)深度优先(递归)
      • +
      • 而且前端很少有完全打乱节点顺序的情况,所以做了四种假设,假设新老 VNode 的开头结尾存在相同节点,一旦命中假设,就避免了一次循环,降低了 diff 的时间复杂度,提高执行效率。如果不幸没有命中假设,则执行遍历,从老的 VNode 中找到新的 VNode 的开始节点
      • +
      • 找到相同节点,则执行 patchVnode,然后将老节点移动到正确的位置
      • +
      • 如果老的 VNode 先于新的 VNode 遍历结束,则剩余的新的 VNode 执行新增节点操作
      • +
      • 如果新的 VNode 先于老的 VNode 遍历结束,则剩余的老的 VNode 执行删除操纵,移除这些老节点
      • +
      +
    • +
    • 如果新的 VNode 有孩子,老的 VNode 没孩子,则新增这些新孩子节点
    • +
    • 如果老的 VNode 有孩子,新的 VNode 没孩子,则删除这些老孩子节点
    • +
    • 剩下一种就是更新文本节点
    • +
    +
  • +
  • 如果新的 VNode 不存在,老的 VNode 存在,则调用 destroy,销毁老节点
  • +
+

碎碎念

好了,到这里,Vue 源码解读系列就结束了,如果你认认真真的读完整个系列的文章,相信你对 Vue 源码已经相当熟悉了,不论是从宏观层面理解,还是某些细节方面的详解,应该都没问题。即使有些细节现在不清楚,但是当遇到问题时,你也能一眼看出来该去源码的什么位置去找答案。

+

到这里你可以试着在自己的脑海中复述一下 Vue 的整个执行流程。过程很重要,但 总结 才是最后的升华时刻。如果在哪个环节卡住了,可再回去读相应的部分就可以了。

+

还记得系列的第一篇文章中提到的目标吗?相信阅读几遍下来,你一定可以在自己的简历中写到:精通 Vue 框架的源码原理

+

接下来会开始 Vue 的手写系列。

+
+

作者:李永宁

+

链接:https://juejin.cn/user/1028798616461326

+

来源:掘金

+

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

+
+

作者介绍

+

祝君无Bug~

Author: 淳淳同学
Link: https://leedebug.github.io/2021/07/05/Vue%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB%EF%BC%88%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93%EF%BC%89/
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Announcement
This is my Blog
Contents
  1. 1. (1)前言
  2. 2. (2)Vue 初始化过程
    1. 2.0.1. Vue 的初始化过程(new Vue(options))都做了什么?
  • 3. (3)响应式原理
    1. 3.0.1. Vue 响应式原理是怎么实现的?
    2. 3.0.2. methods、computed 和 watch 有什么区别?
  • 4. (4)异步更新
    1. 4.0.1. Vue 的异步更新机制是如何实现的?
    2. 4.0.2. Vue 的 nextTick API 是如何实现的?
  • 5. (5)全局 API
    1. 5.0.1. Vue.use(plugin) 做了什么?
    2. 5.0.2. Vue.mixin(options) 做了什么?
    3. 5.0.3. Vue.component(compName, Comp) 做了什么?
    4. 5.0.4. Vue.directive(‘my-directive’, {xx}) 做了什么?
    5. 5.0.5. Vue.filter(‘my-filter’, function(val) {xx}) 做了什么?
    6. 5.0.6. Vue.extend(options) 做了什么?
    7. 5.0.7. Vue.set(target, key, val) 做了什么?
    8. 5.0.8. 面试官 问:Vue.delete(target, key) 做了什么?
    9. 5.0.9. Vue.nextTick(cb) 做了什么?
  • 6. (6)实例方法
    1. 6.0.1. 面试官 问:vm.$set(obj, key, val) 做了什么?
    2. 6.0.2. vm.$delete(obj, key) 做了什么?
    3. 6.0.3. vm.$watch(expOrFn, callback, [options]) 做了什么?
    4. 6.0.4. vm.$on(event, callback) 做了什么?
    5. 6.0.5. vm.$emit(eventName, […args]) 做了什么?
    6. 6.0.6. vm.$off([event, callback]) 做了什么?
    7. 6.0.7. vm.$once(event, callback) 做了什么?
    8. 6.0.8. vm._update(vnode, hydrating) 做了什么?
    9. 6.0.9. vm.$forceUpdate() 做了什么?
    10. 6.0.10. vm.$destroy() 做了什么?
    11. 6.0.11. vm.$nextTick(cb) 做了什么?
    12. 6.0.12. vm._render 做了什么?
  • 7. (7)Hook Event
    1. 7.0.1. 什么是 Hook Event?
    2. 7.0.2. Hook Event 是如果实现的?
  • 8. (8)编译器 之 解析
    1. 8.0.1. 面试官 问:简单说一下 Vue 的编译器都做了什么?
    2. 8.0.2. 详细说一说编译器的解析过程,它是怎么将 html 字符串模版变成 AST 对象的?
  • 9. (9)编译器 之 优化
    1. 9.0.1. 简单说一下 Vue 的编译器都做了什么?
    2. 9.0.2. 详细说一下静态标记的过程
    3. 9.0.3. 什么样的节点才可以被标记为静态节点?
  • 10. (10)编译器 之 生成渲染函数
    1. 10.0.1. 简单说一下 Vue 的编译器都做了什么?
    2. 10.0.2. 详细说一下渲染函数的生成过程
    3. 10.0.3. 碎碎念
  • 11. (11)render helper
    1. 11.0.1. 一个组件是如何变成 VNode?
  • 12. (12)patch
    1. 12.0.1. 你能说一说 Vue 的 patch 算法吗?
    2. 12.0.2. 碎碎念
  • 13. 作者介绍
  • 14. 祝君无Bug~
  • Recent Posts
    \ No newline at end of file diff --git a/2024/12/30/hello-world/index.html b/2024/12/30/hello-world/index.html new file mode 100644 index 000000000..47a4ffd15 --- /dev/null +++ b/2024/12/30/hello-world/index.html @@ -0,0 +1,174 @@ +Hello World | 淳淳同学的个人博客 + + + + + + + + + + + +

    Hello World

    Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

    +

    Quick Start

    Create a new post

    1
    $ hexo new "My New Post"
    + +

    More info: Writing

    +

    Run server

    1
    $ hexo server
    + +

    More info: Server

    +

    Generate static files

    1
    $ hexo generate
    + +

    More info: Generating

    +

    Deploy to remote sites

    1
    $ hexo deploy
    + +

    More info: Deployment

    +

    祝君无Bug~

    Author: 淳淳同学
    Link: https://leedebug.github.io/2024/12/30/hello-world/
    Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
    \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 3abd7e0da..000000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# LeeDebug.github.io - -#### [淳淳同学的博客](https://leedebug.github.io/) diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 4eac47896..000000000 --- a/_config.yml +++ /dev/null @@ -1,111 +0,0 @@ -# Hexo Configuration -## Docs: https://hexo.io/docs/configuration.html -## Source: https://github.com/hexojs/hexo/ - -# Site 基本设置 -title: '淳淳同学的个人博客' -subtitle: '人不可一日无事' -description: '个人博客' -keywords: -author: 淳淳同学 -language: en -timezone: '' - -# URL -## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' -url: https://leedebug.github.io -# url: https://blog.leedebug.cn -root: / -permalink: :year/:month/:day/:title/ -permalink_defaults: -pretty_urls: - trailing_index: true # Set to false to remove trailing 'index.html' from permalinks - trailing_html: true # Set to false to remove trailing '.html' from permalinks - -# Directory -source_dir: source -public_dir: public -tag_dir: tags -archive_dir: archives -category_dir: categories -code_dir: downloads/code -i18n_dir: :lang -skip_render: - -# Writing -new_post_name: :title.md # File name of new posts -default_layout: post -titlecase: false # Transform title into titlecase -external_link: - enable: true # Open external links in new tab - field: site # Apply to the whole site - exclude: '' -filename_case: 0 -render_drafts: false -post_asset_folder: true -relative_link: false -future: true -highlight: - enable: true - line_number: true - auto_detect: false - tab_replace: '' - wrap: true - hljs: false - -# Home page setting -# path: Root path for your blogs index page. (default = '') -# per_page: Posts displayed per page. (0 = disable pagination) -# order_by: Posts order. (Order by date descending by default) -index_generator: - path: '' - per_page: 10 - order_by: -date - -# Category & Tag -default_category: uncategorized -category_map: -tag_map: - -# Metadata elements -## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta -meta_generator: true - -# Date / Time format -## Hexo uses Moment.js to parse and display date -## You can customize the date format as defined in -## http://momentjs.com/docs/#/displaying/format/ -date_format: YYYY-MM-DD -time_format: HH:mm:ss -## Use post's date for updated date unless set in front-matter -use_date_for_updated: false - -# Pagination -## Set per_page to 0 to disable pagination -per_page: 10 -pagination_dir: page - -# Include / Exclude file(s) -## include:/exclude: options only apply to the 'source/' folder -include: -exclude: -ignore: - -# Extensions -## Plugins: https://hexo.io/plugins/ - -## Themes: https://hexo.io/themes/ -# theme: landscape -# theme: hexo-theme-next -# theme: hexo-theme-butterfly-3.3.0 -theme: hexo-theme-butterfly - -# Deployment -## Docs: https://hexo.io/docs/deployment.html -deploy: - type: git - repo: git@github.com:LeeDebug/LeeDebug.github.io.git - # repo: https://gitee.com/LeeDebug/LeeDebug.git - branch: master - - diff --git a/archives/2020/06/index.html b/archives/2020/06/index.html new file mode 100644 index 000000000..38c274643 --- /dev/null +++ b/archives/2020/06/index.html @@ -0,0 +1,237 @@ +June 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2020/07/index.html b/archives/2020/07/index.html new file mode 100644 index 000000000..6ecf87f22 --- /dev/null +++ b/archives/2020/07/index.html @@ -0,0 +1,237 @@ +July 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2020/08/index.html b/archives/2020/08/index.html new file mode 100644 index 000000000..858c66c83 --- /dev/null +++ b/archives/2020/08/index.html @@ -0,0 +1,237 @@ +August 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 1
    2020
    2020前端面试
    2020前端面试
    \ No newline at end of file diff --git a/archives/2020/10/index.html b/archives/2020/10/index.html new file mode 100644 index 000000000..94dbe0422 --- /dev/null +++ b/archives/2020/10/index.html @@ -0,0 +1,237 @@ +October 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 1
    2020
    qiankun 2.0.24 爬坑记录
    qiankun 2.0.24 爬坑记录
    \ No newline at end of file diff --git a/archives/2020/11/index.html b/archives/2020/11/index.html new file mode 100644 index 000000000..29f50c1a5 --- /dev/null +++ b/archives/2020/11/index.html @@ -0,0 +1,237 @@ +November 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 2
    2020
    jsdelivr
    jsdelivr
    中央事件总线插件vue-bus-ts
    中央事件总线插件vue-bus-ts
    \ No newline at end of file diff --git a/archives/2020/12/index.html b/archives/2020/12/index.html new file mode 100644 index 000000000..011cd59f1 --- /dev/null +++ b/archives/2020/12/index.html @@ -0,0 +1,237 @@ +December 2020 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2020/index.html b/archives/2020/index.html new file mode 100644 index 000000000..eee081ba2 --- /dev/null +++ b/archives/2020/index.html @@ -0,0 +1,237 @@ +2020 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2020/page/2/index.html b/archives/2020/page/2/index.html new file mode 100644 index 000000000..aeffa35cf --- /dev/null +++ b/archives/2020/page/2/index.html @@ -0,0 +1,237 @@ +2020 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/01/index.html b/archives/2021/01/index.html new file mode 100644 index 000000000..b691a9555 --- /dev/null +++ b/archives/2021/01/index.html @@ -0,0 +1,237 @@ +January 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/02/index.html b/archives/2021/02/index.html new file mode 100644 index 000000000..f1fe75ac0 --- /dev/null +++ b/archives/2021/02/index.html @@ -0,0 +1,237 @@ +February 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/03/index.html b/archives/2021/03/index.html new file mode 100644 index 000000000..f7c2232a5 --- /dev/null +++ b/archives/2021/03/index.html @@ -0,0 +1,237 @@ +March 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/04/index.html b/archives/2021/04/index.html new file mode 100644 index 000000000..3bc4ec6c4 --- /dev/null +++ b/archives/2021/04/index.html @@ -0,0 +1,237 @@ +April 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 2
    2021
    JavaScript运算符优先级
    JavaScript运算符优先级
    预防XSS攻击插件 js-xss
    预防XSS攻击插件 js-xss
    \ No newline at end of file diff --git a/archives/2021/05/index.html b/archives/2021/05/index.html new file mode 100644 index 000000000..d6d2ae20d --- /dev/null +++ b/archives/2021/05/index.html @@ -0,0 +1,237 @@ +May 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/07/index.html b/archives/2021/07/index.html new file mode 100644 index 000000000..fe4ae5f45 --- /dev/null +++ b/archives/2021/07/index.html @@ -0,0 +1,237 @@ +July 2021 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 1
    2021
    Vue源码解读(知识点总结)
    Vue源码解读(知识点总结)
    \ No newline at end of file diff --git a/archives/2021/index.html b/archives/2021/index.html new file mode 100644 index 000000000..89b3099ff --- /dev/null +++ b/archives/2021/index.html @@ -0,0 +1,237 @@ +2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2021/page/2/index.html b/archives/2021/page/2/index.html new file mode 100644 index 000000000..79e75ff42 --- /dev/null +++ b/archives/2021/page/2/index.html @@ -0,0 +1,237 @@ +2021 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/2024/12/index.html b/archives/2024/12/index.html new file mode 100644 index 000000000..44cfb350c --- /dev/null +++ b/archives/2024/12/index.html @@ -0,0 +1,237 @@ +December 2024 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 1
    2024
    Hello World
    Hello World
    \ No newline at end of file diff --git a/archives/2024/index.html b/archives/2024/index.html new file mode 100644 index 000000000..c17e9189d --- /dev/null +++ b/archives/2024/index.html @@ -0,0 +1,237 @@ +2024 | 淳淳同学的个人博客 + + + + + + + + + +
    All Articles - 1
    2024
    Hello World
    Hello World
    \ No newline at end of file diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 000000000..7c7089f42 --- /dev/null +++ b/archives/index.html @@ -0,0 +1,237 @@ +时间轴 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 000000000..ab4b6bf46 --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,237 @@ +时间轴 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/page/3/index.html b/archives/page/3/index.html new file mode 100644 index 000000000..c0759b3ae --- /dev/null +++ b/archives/page/3/index.html @@ -0,0 +1,237 @@ +时间轴 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/archives/page/4/index.html b/archives/page/4/index.html new file mode 100644 index 000000000..e3a3c2a87 --- /dev/null +++ b/archives/page/4/index.html @@ -0,0 +1,237 @@ +时间轴 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/source/assets/images/avator.jpg b/assets/images/avator.jpg similarity index 100% rename from source/assets/images/avator.jpg rename to assets/images/avator.jpg diff --git a/categories/CSS/index.html b/categories/CSS/index.html new file mode 100644 index 000000000..250039f73 --- /dev/null +++ b/categories/CSS/index.html @@ -0,0 +1,237 @@ +Category: CSS | 淳淳同学的个人博客 + + + + + + + + + +
    Category - CSS
    2021
    linear-gradient函数实现线性渐变
    linear-gradient函数实现线性渐变
    \ No newline at end of file diff --git a/categories/Git/index.html b/categories/Git/index.html new file mode 100644 index 000000000..47c355a90 --- /dev/null +++ b/categories/Git/index.html @@ -0,0 +1,237 @@ +Category: Git | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/categories/JavaScript/index.html b/categories/JavaScript/index.html new file mode 100644 index 000000000..9913f4627 --- /dev/null +++ b/categories/JavaScript/index.html @@ -0,0 +1,237 @@ +Category: JavaScript | 淳淳同学的个人博客 + + + + + + + + + +
    Category - JavaScript
    2021
    JavaScript运算符优先级
    JavaScript运算符优先级
    \ No newline at end of file diff --git a/categories/LeetCode/index.html b/categories/LeetCode/index.html new file mode 100644 index 000000000..2ef66fc38 --- /dev/null +++ b/categories/LeetCode/index.html @@ -0,0 +1,237 @@ +Category: LeetCode | 淳淳同学的个人博客 + + + + + + + + + +
    Category - LeetCode
    2020
    LeetCode常见题
    LeetCode常见题
    \ No newline at end of file diff --git a/categories/Socket/index.html b/categories/Socket/index.html new file mode 100644 index 000000000..2cbf3fe0f --- /dev/null +++ b/categories/Socket/index.html @@ -0,0 +1,237 @@ +Category: Socket | 淳淳同学的个人博客 + + + + + + + + + +
    Category - Socket
    2021
    【转】看完让你彻底搞懂Websocket原理
    【转】看完让你彻底搞懂Websocket原理
    \ No newline at end of file diff --git a/categories/VSCode/index.html b/categories/VSCode/index.html new file mode 100644 index 000000000..7b5602f88 --- /dev/null +++ b/categories/VSCode/index.html @@ -0,0 +1,237 @@ +Category: VSCode | 淳淳同学的个人博客 + + + + + + + + + +
    Category - VSCode
    2021
    VSCode注释高亮插件 Better Comments
    VSCode注释高亮插件 Better Comments
    \ No newline at end of file diff --git a/categories/Vue/index.html b/categories/Vue/index.html new file mode 100644 index 000000000..0917bdb11 --- /dev/null +++ b/categories/Vue/index.html @@ -0,0 +1,237 @@ +Category: Vue | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/categories/Webpack/index.html b/categories/Webpack/index.html new file mode 100644 index 000000000..ca68610d9 --- /dev/null +++ b/categories/Webpack/index.html @@ -0,0 +1,237 @@ +Category: Webpack | 淳淳同学的个人博客 + + + + + + + + + +
    Category - Webpack
    2021
    copy-webpack-plugin处理单独js文件
    copy-webpack-plugin处理单独js文件
    2020
    node打包内存溢出
    node打包内存溢出
    \ No newline at end of file diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 000000000..7f91d7f2b --- /dev/null +++ b/categories/index.html @@ -0,0 +1,239 @@ +分类 | 淳淳同学的个人博客 + + + + + + + + + + + +
    \ No newline at end of file diff --git a/categories/npm/index.html b/categories/npm/index.html new file mode 100644 index 000000000..292ce5bc2 --- /dev/null +++ b/categories/npm/index.html @@ -0,0 +1,237 @@ +Category: npm | 淳淳同学的个人博客 + + + + + + + + + +
    Category - npm
    2020
    js-md5
    js-md5
    \ No newline at end of file diff --git "a/categories/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" "b/categories/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" new file mode 100644 index 000000000..6749675ce --- /dev/null +++ "b/categories/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" @@ -0,0 +1,237 @@ +Category: 个人文档 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/categories/\345\276\256\345\211\215\347\253\257/index.html" "b/categories/\345\276\256\345\211\215\347\253\257/index.html" new file mode 100644 index 000000000..9e95c0223 --- /dev/null +++ "b/categories/\345\276\256\345\211\215\347\253\257/index.html" @@ -0,0 +1,237 @@ +Category: 微前端 | 淳淳同学的个人博客 + + + + + + + + + +
    Category - 微前端
    2020
    qiankun的css样式污染解决方案
    qiankun的css样式污染解决方案
    qiankun 2.0.24 爬坑记录
    qiankun 2.0.24 爬坑记录
    \ No newline at end of file diff --git "a/categories/\346\234\215\345\212\241\345\231\250/index.html" "b/categories/\346\234\215\345\212\241\345\231\250/index.html" new file mode 100644 index 000000000..b472298a8 --- /dev/null +++ "b/categories/\346\234\215\345\212\241\345\231\250/index.html" @@ -0,0 +1,237 @@ +Category: 服务器 | 淳淳同学的个人博客 + + + + + + + + + +
    Category - 服务器
    2020
    Ubuntu 16.04 部署命令
    Ubuntu 16.04 部署命令
    \ No newline at end of file diff --git "a/categories/\347\247\273\345\212\250\347\253\257/index.html" "b/categories/\347\247\273\345\212\250\347\253\257/index.html" new file mode 100644 index 000000000..8b247040a --- /dev/null +++ "b/categories/\347\247\273\345\212\250\347\253\257/index.html" @@ -0,0 +1,237 @@ +Category: 移动端 | 淳淳同学的个人博客 + + + + + + + + + +
    Category - 移动端
    2021
    navigator.userAgent获取当前设备信息
    navigator.userAgent获取当前设备信息
    \ No newline at end of file diff --git "a/categories/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" "b/categories/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" new file mode 100644 index 000000000..9f6dd17df --- /dev/null +++ "b/categories/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" @@ -0,0 +1,237 @@ +Category: 读书笔记 | 淳淳同学的个人博客 + + + + + + + + + +
    Category - 读书笔记
    2020
    你不知道的JavaScript(上卷)
    你不知道的JavaScript(上卷)
    《前端内参》读书笔记
    《前端内参》读书笔记
    \ No newline at end of file diff --git "a/categories/\351\235\242\350\257\225/index.html" "b/categories/\351\235\242\350\257\225/index.html" new file mode 100644 index 000000000..2a4a600df --- /dev/null +++ "b/categories/\351\235\242\350\257\225/index.html" @@ -0,0 +1,237 @@ +Category: 面试 | 淳淳同学的个人博客 + + + + + + + + + +
    Category - 面试
    2021
    2021前端知识点总结
    2021前端知识点总结
    2020
    2020前端面试
    2020前端面试
    \ No newline at end of file diff --git a/source/css/calendar.css b/css/calendar.css similarity index 100% rename from source/css/calendar.css rename to css/calendar.css diff --git a/css/index.css b/css/index.css new file mode 100644 index 000000000..e9a4ad22a --- /dev/null +++ b/css/index.css @@ -0,0 +1,6241 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html { + line-height: 1.15; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +main { + display: block +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +pre { + font-family: monospace, monospace; + font-size: 1em +} + +a { + background-color: transparent +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted +} + +b, +strong { + font-weight: bolder +} + +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em +} + +small { + font-size: 80% +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +img { + border-style: none +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0 +} + +button, +input { + overflow: visible +} + +button, +select { + text-transform: none +} + +[type=button], +[type=reset], +[type=submit], +button { + -webkit-appearance: button +} + +[type=button]::-moz-focus-inner, +[type=reset]::-moz-focus-inner, +[type=submit]::-moz-focus-inner, +button::-moz-focus-inner { + border-style: none; + padding: 0 +} + +[type=button]:-moz-focusring, +[type=reset]:-moz-focusring, +[type=submit]:-moz-focusring, +button:-moz-focusring { + outline: 1px dotted ButtonText +} + +fieldset { + padding: .35em .75em .625em +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal +} + +progress { + vertical-align: baseline +} + +textarea { + overflow: auto +} + +[type=checkbox], +[type=radio] { + box-sizing: border-box; + padding: 0 +} + +[type=number]::-webkit-inner-spin-button, +[type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +details { + display: block +} + +summary { + display: list-item +} + +template { + display: none +} + +[hidden] { + display: none +} +.limit-one-line, +.container .flink .flink-item-name, +.container .flink .flink-item-desc, +#aside-content .card-archives ul.card-archive-list > .card-archive-list-item a span, +#aside-content .card-categories ul.card-category-list > .card-category-list-item a span, +.site-data > a .headline, +#nav #blog-info, +#sidebar #sidebar-menus .menus_items .site-page { + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; +} +.limit-more-line, +.article-sort-item-title, +#recent-posts .recent-post-item >.recent-post-info > .article-title, +#recent-posts .recent-post-item >.recent-post-info > .content, +#aside-content .aside-list > .aside-list-item .content > .name, +#aside-content .aside-list > .aside-list-item .content > .title, +#aside-content .aside-list > .aside-list-item .content > .comment, +#post-info .post-title, +.pagination-related .info .info-1 .info-item-2, +.pagination-related .info .info-2 .info-item-1, +.container figure.gallery-group p, +.container figure.gallery-group .gallery-group-name { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; +} +.fontawesomeIcon, +.custom-hr:before, +#post .post-copyright:before, +#post #post-outdate-notice:before, +.note:not(.no-icon)::before, +.search-dialog hr:before { + display: inline-block; + font-weight: 600; + font-family: 'Font Awesome 6 Free'; + text-rendering: auto; + -webkit-font-smoothing: antialiased; +} +.cardHover, +.layout > div:first-child:not(.nc), +#recent-posts .recent-post-item, +#article-container .shuoshuo-item, +#aside-content .card-widget, +.layout .pagination > *:not(.space) { + background: var(--card-bg); + -webkit-box-shadow: var(--card-box-shadow); + box-shadow: var(--card-box-shadow); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + border-radius: 8px; +} +.cardHover:hover, +.layout > div:first-child:not(.nc):hover, +#recent-posts .recent-post-item:hover, +#article-container .shuoshuo-item:hover, +#aside-content .card-widget:hover, +.layout .pagination > *:not(.space):hover { + -webkit-box-shadow: var(--card-hover-box-shadow); + box-shadow: var(--card-hover-box-shadow); +} +.imgHover, +.article-sort-item-img :first-child, +#recent-posts .recent-post-item .post_cover .post-bg, +#aside-content .aside-list > .aside-list-item .thumbnail :first-child { + width: 100%; + height: 100%; + -webkit-transition: filter 375ms ease-in 0.2s, -webkit-transform 0.6s; + -moz-transition: filter 375ms ease-in 0.2s, -moz-transform 0.6s; + -o-transition: filter 375ms ease-in 0.2s, -o-transform 0.6s; + -ms-transition: filter 375ms ease-in 0.2s, -ms-transform 0.6s; + transition: filter 375ms ease-in 0.2s, transform 0.6s; + object-fit: cover; +} +.imgHover:hover, +.article-sort-item-img :first-child:hover, +#recent-posts .recent-post-item .post_cover .post-bg:hover, +#aside-content .aside-list > .aside-list-item .thumbnail :first-child:hover { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -o-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.postImgHover:hover .cover, +.pagination-related:hover .cover { + opacity: 0.5; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -o-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.postImgHover .cover, +.pagination-related .cover { + width: 100%; + height: 100%; + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transition: all 0.6s, filter 375ms ease-in 0.2s; + -moz-transition: all 0.6s, filter 375ms ease-in 0.2s; + -o-transition: all 0.6s, filter 375ms ease-in 0.2s; + -ms-transition: all 0.6s, filter 375ms ease-in 0.2s; + transition: all 0.6s, filter 375ms ease-in 0.2s; + object-fit: cover; +} +.list-beauty, +.category-lists ul { + list-style: none; +} +.list-beauty li, +.category-lists ul li { + position: relative; + padding: 0.12em 0.4em 0.12em 1.4em; +} +.list-beauty li:hover:before, +.category-lists ul li:hover:before { + border-color: var(--pseudo-hover); +} +.list-beauty li:before, +.category-lists ul li:before { + position: absolute; + top: 0.67em; + left: 0; + width: 0.43em; + height: 0.43em; + border: 0.215em solid #49b1f5; + border-radius: 0.43em; + background: transparent; + content: ''; + cursor: pointer; + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} +.custom-hr, +.search-dialog hr { + position: relative; + margin: 40px auto; + border: 2px dashed var(--hr-border); + width: calc(100% - 4px); +} +.custom-hr:hover:before, +.search-dialog hr:hover:before { + left: calc(95% - 20px); +} +.custom-hr:before, +.search-dialog hr:before { + position: absolute; + top: -10px; + left: 5%; + z-index: 1; + color: var(--hr-before-color); + content: '\f0c4'; + font-size: 20px; + line-height: 1; + -webkit-transition: all 1s ease-in-out; + -moz-transition: all 1s ease-in-out; + -o-transition: all 1s ease-in-out; + -ms-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} +.verticalCenter, +.pagination-related .info .info-1, +.pagination-related .info .info-2 { + position: absolute; + top: 50%; + width: 100%; + -webkit-transform: translate(0, -50%); + -moz-transform: translate(0, -50%); + -o-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); +} +#content-inner, +#footer { + -webkit-animation: bottom-top 1s; + -moz-animation: bottom-top 1s; + -o-animation: bottom-top 1s; + -ms-animation: bottom-top 1s; + animation: bottom-top 1s; +} +#page-header:not(.full_page), +#nav.show { + -webkit-animation: header-effect 1s; + -moz-animation: header-effect 1s; + -o-animation: header-effect 1s; + -ms-animation: header-effect 1s; + animation: header-effect 1s; +} +#site-title, +#site-subtitle { + -webkit-animation: titleScale 1s; + -moz-animation: titleScale 1s; + -o-animation: titleScale 1s; + -ms-animation: titleScale 1s; + animation: titleScale 1s; +} +canvas:not(#ribbon-canvas), +#web_bg { + -webkit-animation: to_show 4s; + -moz-animation: to_show 4s; + -o-animation: to_show 4s; + -ms-animation: to_show 4s; + animation: to_show 4s; +} +#ribbon-canvas { + -webkit-animation: ribbon_to_show 4s; + -moz-animation: ribbon_to_show 4s; + -o-animation: ribbon_to_show 4s; + -ms-animation: ribbon_to_show 4s; + animation: ribbon_to_show 4s; +} +#sidebar-menus.open > :nth-child(1) { + -webkit-animation: sidebarItem 0.2s; + -moz-animation: sidebarItem 0.2s; + -o-animation: sidebarItem 0.2s; + -ms-animation: sidebarItem 0.2s; + animation: sidebarItem 0.2s; +} +#sidebar-menus.open > :nth-child(2) { + -webkit-animation: sidebarItem 0.4s; + -moz-animation: sidebarItem 0.4s; + -o-animation: sidebarItem 0.4s; + -ms-animation: sidebarItem 0.4s; + animation: sidebarItem 0.4s; +} +#sidebar-menus.open > :nth-child(3) { + -webkit-animation: sidebarItem 0.6s; + -moz-animation: sidebarItem 0.6s; + -o-animation: sidebarItem 0.6s; + -ms-animation: sidebarItem 0.6s; + animation: sidebarItem 0.6s; +} +#sidebar-menus.open > :nth-child(4) { + -webkit-animation: sidebarItem 0.8s; + -moz-animation: sidebarItem 0.8s; + -o-animation: sidebarItem 0.8s; + -ms-animation: sidebarItem 0.8s; + animation: sidebarItem 0.8s; +} +.scroll-down-effects { + -webkit-animation: scroll-down-effect 1.5s infinite; + -moz-animation: scroll-down-effect 1.5s infinite; + -o-animation: scroll-down-effect 1.5s infinite; + -ms-animation: scroll-down-effect 1.5s infinite; + animation: scroll-down-effect 1.5s infinite; +} +.reward-main { + -webkit-animation: donate_effcet 0.3s 0.1s ease both; + -moz-animation: donate_effcet 0.3s 0.1s ease both; + -o-animation: donate_effcet 0.3s 0.1s ease both; + -ms-animation: donate_effcet 0.3s 0.1s ease both; + animation: donate_effcet 0.3s 0.1s ease both; +} +@-moz-keyframes scroll-down-effect { + 0% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } + 50% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, -16px); + -moz-transform: translate(0, -16px); + -o-transform: translate(0, -16px); + -ms-transform: translate(0, -16px); + transform: translate(0, -16px); + } + 100% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } +} +@-webkit-keyframes scroll-down-effect { + 0% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } + 50% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, -16px); + -moz-transform: translate(0, -16px); + -o-transform: translate(0, -16px); + -ms-transform: translate(0, -16px); + transform: translate(0, -16px); + } + 100% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } +} +@-o-keyframes scroll-down-effect { + 0% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } + 50% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, -16px); + -moz-transform: translate(0, -16px); + -o-transform: translate(0, -16px); + -ms-transform: translate(0, -16px); + transform: translate(0, -16px); + } + 100% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } +} +@keyframes scroll-down-effect { + 0% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } + 50% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, -16px); + -moz-transform: translate(0, -16px); + -o-transform: translate(0, -16px); + -ms-transform: translate(0, -16px); + transform: translate(0, -16px); + } + 100% { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } +} +@-moz-keyframes header-effect { + 0% { + -webkit-transform: translateY(-35px); + -moz-transform: translateY(-35px); + -o-transform: translateY(-35px); + -ms-transform: translateY(-35px); + transform: translateY(-35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes header-effect { + 0% { + -webkit-transform: translateY(-35px); + -moz-transform: translateY(-35px); + -o-transform: translateY(-35px); + -ms-transform: translateY(-35px); + transform: translateY(-35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes header-effect { + 0% { + -webkit-transform: translateY(-35px); + -moz-transform: translateY(-35px); + -o-transform: translateY(-35px); + -ms-transform: translateY(-35px); + transform: translateY(-35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes header-effect { + 0% { + -webkit-transform: translateY(-35px); + -moz-transform: translateY(-35px); + -o-transform: translateY(-35px); + -ms-transform: translateY(-35px); + transform: translateY(-35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-moz-keyframes bottom-top { + 0% { + -webkit-transform: translateY(35px); + -moz-transform: translateY(35px); + -o-transform: translateY(35px); + -ms-transform: translateY(35px); + transform: translateY(35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes bottom-top { + 0% { + -webkit-transform: translateY(35px); + -moz-transform: translateY(35px); + -o-transform: translateY(35px); + -ms-transform: translateY(35px); + transform: translateY(35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes bottom-top { + 0% { + -webkit-transform: translateY(35px); + -moz-transform: translateY(35px); + -o-transform: translateY(35px); + -ms-transform: translateY(35px); + transform: translateY(35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes bottom-top { + 0% { + -webkit-transform: translateY(35px); + -moz-transform: translateY(35px); + -o-transform: translateY(35px); + -ms-transform: translateY(35px); + transform: translateY(35px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-moz-keyframes titleScale { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-webkit-keyframes titleScale { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-o-keyframes titleScale { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@keyframes titleScale { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-moz-keyframes search_close { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@-webkit-keyframes search_close { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@-o-keyframes search_close { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@keyframes search_close { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@-moz-keyframes to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + } +} +@-webkit-keyframes to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + } +} +@-o-keyframes to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + } +} +@keyframes to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + } +} +@-moz-keyframes to_hide { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } +} +@-webkit-keyframes to_hide { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } +} +@-o-keyframes to_hide { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } +} +@keyframes to_hide { + 0% { + opacity: 1; + -ms-filter: none; + filter: none; + } + 100% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } +} +@-moz-keyframes ribbon_to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + } +} +@-webkit-keyframes ribbon_to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + } +} +@-o-keyframes ribbon_to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + } +} +@keyframes ribbon_to_show { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + } + 100% { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + } +} +@-moz-keyframes avatar_turn_around { + from { + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + to { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-webkit-keyframes avatar_turn_around { + from { + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + to { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-o-keyframes avatar_turn_around { + from { + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + to { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes avatar_turn_around { + from { + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + to { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-moz-keyframes sub_menus { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(10px); + -moz-transform: translateY(10px); + -o-transform: translateY(10px); + -ms-transform: translateY(10px); + transform: translateY(10px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes sub_menus { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(10px); + -moz-transform: translateY(10px); + -o-transform: translateY(10px); + -ms-transform: translateY(10px); + transform: translateY(10px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes sub_menus { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(10px); + -moz-transform: translateY(10px); + -o-transform: translateY(10px); + -ms-transform: translateY(10px); + transform: translateY(10px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes sub_menus { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(10px); + -moz-transform: translateY(10px); + -o-transform: translateY(10px); + -ms-transform: translateY(10px); + transform: translateY(10px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-moz-keyframes donate_effcet { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(-20px); + -moz-transform: translateY(-20px); + -o-transform: translateY(-20px); + -ms-transform: translateY(-20px); + transform: translateY(-20px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes donate_effcet { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(-20px); + -moz-transform: translateY(-20px); + -o-transform: translateY(-20px); + -ms-transform: translateY(-20px); + transform: translateY(-20px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes donate_effcet { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(-20px); + -moz-transform: translateY(-20px); + -o-transform: translateY(-20px); + -ms-transform: translateY(-20px); + transform: translateY(-20px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes donate_effcet { + 0% { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translateY(-20px); + -moz-transform: translateY(-20px); + -o-transform: translateY(-20px); + -ms-transform: translateY(-20px); + transform: translateY(-20px); + } + 100% { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-moz-keyframes sidebarItem { + 0% { + -webkit-transform: translateX(200px); + -moz-transform: translateX(200px); + -o-transform: translateX(200px); + -ms-transform: translateX(200px); + transform: translateX(200px); + } + 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } +} +@-webkit-keyframes sidebarItem { + 0% { + -webkit-transform: translateX(200px); + -moz-transform: translateX(200px); + -o-transform: translateX(200px); + -ms-transform: translateX(200px); + transform: translateX(200px); + } + 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } +} +@-o-keyframes sidebarItem { + 0% { + -webkit-transform: translateX(200px); + -moz-transform: translateX(200px); + -o-transform: translateX(200px); + -ms-transform: translateX(200px); + transform: translateX(200px); + } + 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } +} +@keyframes sidebarItem { + 0% { + -webkit-transform: translateX(200px); + -moz-transform: translateX(200px); + -o-transform: translateX(200px); + -ms-transform: translateX(200px); + transform: translateX(200px); + } + 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } +} +:root { + --global-font-size: 14px; + --global-bg: #fff; + --font-color: #4c4948; + --hr-border: #a4d8fa; + --hr-before-color: #80c8f8; + --search-bg: #f6f8fa; + --search-input-color: #4c4948; + --search-a-color: #4c4948; + --preloader-bg: #37474f; + --preloader-color: #fff; + --tab-border-color: #f0f0f0; + --tab-botton-bg: #f0f0f0; + --tab-botton-color: #1f2d3d; + --tab-button-hover-bg: #dcdcdc; + --tab-button-active-bg: #fff; + --card-bg: #fff; + --card-meta: #858585; + --sidebar-bg: #f6f8fa; + --sidebar-menu-bg: #fff; + --btn-hover-color: #ff7242; + --btn-color: #fff; + --btn-bg: #49b1f5; + --text-bg-hover: rgba(73,177,245,0.7); + --light-grey: #eee; + --dark-grey: #cacaca; + --white: #fff; + --text-highlight-color: #1f2d3d; + --blockquote-color: #6a737d; + --blockquote-bg: rgba(73,177,245,0.1); + --reward-pop: #f5f5f5; + --toc-link-color: #666261; + --card-box-shadow: 0 3px 8px 6px rgba(7,17,27,0.05); + --card-hover-box-shadow: 0 3px 8px 6px rgba(7,17,27,0.09); + --pseudo-hover: #ff7242; + --headline-presudo: #a0a0a0; + --scrollbar-color: #49b1f5; + --default-bg-color: #49b1f5; + --zoom-bg: #fff; + --mark-bg: rgba(0,0,0,0.3); +} +body { + position: relative; + overflow-y: scroll; + min-height: 100%; + background: var(--global-bg); + color: var(--font-color); + font-size: var(--global-font-size); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', 'Microsoft JhengHei', sans-serif; + line-height: 2; + -webkit-tap-highlight-color: rgba(0,0,0,0); + scroll-behavior: smooth; +} +@-moz-document url-prefix() { + * { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-color) transparent; + } +} +*::-webkit-scrollbar { + width: 5px; + height: 5px; +} +*::-webkit-scrollbar-thumb { + background: var(--scrollbar-color); +} +*::-webkit-scrollbar-track { + background-color: transparent; +} +input::placeholder { + color: var(--font-color); +} +#web_bg { + position: fixed; + z-index: -999; + width: 100%; + height: 100%; + background-attachment: local; + background-position: center; + background-size: cover; + background-repeat: no-repeat; +} +h1, +h2, +h3, +h4, +h5, +h6 { + position: relative; + margin: 20px 0 14px; + color: var(--text-highlight-color); + font-weight: bold; +} +h1 code, +h2 code, +h3 code, +h4 code, +h5 code, +h6 code { + font-size: inherit !important; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.table-wrap { + overflow-x: scroll; + margin: 0 0 20px; + border-radius: 5px; +} +.table-wrap table { + border-radius: 5px; +} +.table-wrap table thead > tr:first-child th:first-child { + border-top-left-radius: 5px; +} +.table-wrap table thead > tr:first-child th:last-child { + border-top-right-radius: 5px; +} +.table-wrap table tbody > tr:last-child td:first-child { + border-bottom-left-radius: 5px; +} +.table-wrap table tbody > tr:last-child td:last-child { + border-bottom-right-radius: 5px; +} +table { + display: table; + width: 100%; + border-spacing: 0; + border-collapse: separate; + border-top: 1px solid var(--light-grey); + border-left: 1px solid var(--light-grey); + empty-cells: show; +} +table thead { + background: rgba(153,169,191,0.1); +} +table th, +table td { + padding: 6px 12px; + border: 1px solid var(--light-grey); + border-top: none; + border-left: none; + vertical-align: middle; +} +*::selection { + background: #00c4b6; + color: #f7f7f7; +} +button { + padding: 0; + outline: 0; + border: none; + background: none; + cursor: pointer; + touch-action: manipulation; +} +a { + color: #99a9bf; + text-decoration: none; + word-wrap: break-word; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + -o-transition: all 0.2s; + -ms-transition: all 0.2s; + transition: all 0.2s; + overflow-wrap: break-word; +} +a:hover { + color: #49b1f5; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +img[src=''], +img:not([src]) { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); +} +.img-alt { + margin: -10px 0 10px; + color: #858585; +} +.img-alt:hover { + text-decoration: none !important; +} +blockquote { + margin: 0 0 20px; + padding: 7px 15px; + border-left: 4px solid #49b1f5; + background-color: var(--blockquote-bg); + color: var(--blockquote-color); + border-radius: 6px; +} +blockquote footer cite:before { + padding: 0 5px; + content: '—'; +} +blockquote > :last-child { + margin-bottom: 0 !important; +} +:root { + --hl-color: #90a4ae; + --hl-bg: #f6f8fa; + --hltools-bg: #e6ebf1; + --hltools-color: #90a4ae; + --hlnumber-bg: #f6f8fa; + --hlnumber-color: rgba(144,164,174,0.5); + --hlscrollbar-bg: #dce4eb; + --hlexpand-bg: linear-gradient(180deg, rgba(246,248,250,0.6), rgba(246,248,250,0.9)); +} +[data-theme='dark'] { + --hl-color: rgba(255,255,255,0.7); + --hl-bg: #171717; + --hltools-bg: #1a1a1a; + --hltools-color: #90a4ae; + --hlnumber-bg: #171717; + --hlnumber-color: rgba(255,255,255,0.4); + --hlscrollbar-bg: #1f1f1f; + --hlexpand-bg: linear-gradient(180deg, rgba(23,23,23,0.6), rgba(23,23,23,0.9)); +} +@-moz-document url-prefix() { + scrollbar-color: var(--hlscrollbar-bg) transparent; +} +figure.highlight table::-webkit-scrollbar-thumb { + background: var(--hlscrollbar-bg); +} +figure.highlight pre .deletion { + color: #bf42bf; +} +figure.highlight pre .addition { + color: #105ede; +} +figure.highlight pre .meta { + color: #7c4dff; +} +figure.highlight pre .comment { + color: rgba(149,165,166,0.8); +} +figure.highlight pre .variable, +figure.highlight pre .attribute, +figure.highlight pre .regexp, +figure.highlight pre .ruby .constant, +figure.highlight pre .xml .tag .title, +figure.highlight pre .xml .pi, +figure.highlight pre .xml .doctype, +figure.highlight pre .html .doctype, +figure.highlight pre .css .id, +figure.highlight pre .tag .name, +figure.highlight pre .css .class, +figure.highlight pre .css .pseudo { + color: #e53935; +} +figure.highlight pre .tag { + color: #39adb5; +} +figure.highlight pre .number, +figure.highlight pre .preprocessor, +figure.highlight pre .literal, +figure.highlight pre .params, +figure.highlight pre .constant, +figure.highlight pre .command { + color: #f76d47; +} +figure.highlight pre .built_in { + color: #ffb62c; +} +figure.highlight pre .ruby .class .title, +figure.highlight pre .css .rules .attribute, +figure.highlight pre .string, +figure.highlight pre .value, +figure.highlight pre .inheritance, +figure.highlight pre .header, +figure.highlight pre .ruby .symbol, +figure.highlight pre .xml .cdata, +figure.highlight pre .special, +figure.highlight pre .number, +figure.highlight pre .formula { + color: #91b859; +} +figure.highlight pre .keyword, +figure.highlight pre .title, +figure.highlight pre .css .hexcolor { + color: #39adb5; +} +figure.highlight pre .function, +figure.highlight pre .python .decorator, +figure.highlight pre .python .title, +figure.highlight pre .ruby .function .title, +figure.highlight pre .ruby .title .keyword, +figure.highlight pre .perl .sub, +figure.highlight pre .javascript .title, +figure.highlight pre .coffeescript .title { + color: #6182b8; +} +figure.highlight pre .tag .attr, +figure.highlight pre .javascript .function { + color: #7c4dff; +} +.container figure.highlight .line.marked { + background-color: rgba(128,203,196,0.251); +} +.container figure.highlight table { + display: block; + overflow: auto; + border: none; +} +.container figure.highlight table td { + padding: 0; + border: none; +} +.container figure.highlight .gutter pre { + padding-right: 10px; + padding-left: 10px; + background-color: var(--hlnumber-bg); + color: var(--hlnumber-color); + text-align: right; +} +.container figure.highlight .code pre { + padding-right: 10px; + padding-left: 10px; + width: 100%; +} +.container pre, +.container figure.highlight { + overflow: auto; + margin: 0 0 20px; + padding: 0; + background: var(--hl-bg); + color: var(--hl-color); + line-height: 1.6; +} +.container pre, +.container code { + font-size: var(--global-font-size); + font-family: consolas, Menlo, monospace, 'PingFang SC', 'Microsoft JhengHei', sans-serif !important; + border-radius: 6px; +} +.container code { + padding: 2px 5px; + background: rgba(27,31,35,0.05); + color: #f47466; +} +.container pre { + padding: 10px 20px; +} +.container pre code { + padding: 0; + background: none; + color: var(--hl-color); + text-shadow: none; +} +.container figure.highlight { + position: relative; + border-radius: 6px; +} +.container figure.highlight pre { + margin: 0; + padding: 8px 0; + border: none; +} +.container figure.highlight figcaption, +.container figure.highlight .caption { + padding: 6px 0 2px 14px; + font-size: var(--global-font-size); + line-height: 1em; +} +.container figure.highlight figcaption a, +.container figure.highlight .caption a { + float: right; + padding-right: 10px; + color: var(--hl-color); +} +.container figure.highlight figcaption a:hover, +.container figure.highlight .caption a:hover { + border-bottom-color: var(--hl-color); +} +.container figure.highlight.copy-true { + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; + -webkit-user-select: all; +} +.container figure.highlight.copy-true > table, +.container figure.highlight.copy-true > pre { + display: block !important; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); +} +.container .highlight-tools { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + padding: 0 8px; + min-height: 24px; + height: 2.15em; + background: var(--hltools-bg); + color: var(--hltools-color); + font-size: var(--global-font-size); + overflow: hidden; +} +.container .highlight-tools > * { + padding: 5px; +} +.container .highlight-tools i { + cursor: pointer; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +.container .highlight-tools i:hover { + color: #49b1f5; +} +.container .highlight-tools.closed ~ * { + display: none; +} +.container .highlight-tools.closed .expand { + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg); +} +.container .highlight-tools .code-lang { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + text-transform: uppercase; + font-weight: bold; + font-size: 1.15em; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + padding: 2px; +} +.container .highlight-tools .copy-notice { + padding-right: 2px; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: opacity 0.4s; + -moz-transition: opacity 0.4s; + -o-transition: opacity 0.4s; + -ms-transition: opacity 0.4s; + transition: opacity 0.4s; +} +.container .highlight-tools .code-lang { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +.container .gutter { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; +} +.container .gist table { + width: auto; +} +.container .gist table td { + border: none; +} +.container figure.highlight { + margin: 0 0 24px; + border-radius: 7px; + -webkit-box-shadow: 0 5px 10px 0 rgba(144,164,174,0.4); + box-shadow: 0 5px 10px 0 rgba(144,164,174,0.4); + -webkit-transform: translateZ(0); +} +.container figure.highlight .highlight-tools .macStyle { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; +} +.container figure.highlight .highlight-tools .macStyle > * { + margin-right: 8px; + width: 12px; + height: 12px; + border-radius: 50%; +} +.container figure.highlight .highlight-tools .macStyle > :last-child { + margin-right: 5px; +} +.container figure.highlight .highlight-tools .macStyle .mac-close { + background: #fc625d; +} +.container figure.highlight .highlight-tools .macStyle .mac-minimize { + background: #fdbc40; +} +.container figure.highlight .highlight-tools .macStyle .mac-maximize { + background: #35cd4b; +} +.container figure.highlight .highlight-tools > :nth-child(2) { + -webkit-box-ordinal-group: 8; + -moz-box-ordinal-group: 8; + -o-box-ordinal-group: 8; + -ms-flex-order: 8; + -webkit-order: 8; + order: 8; +} +.container figure.highlight .highlight-tools.closed .expand { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.article-sort { + margin-left: 10px; + padding-left: 20px; + border-left: 2px solid #aadafa; +} +.article-sort-title { + position: relative; + margin-left: 10px; + padding-bottom: 20px; + padding-left: 20px; + font-size: 1.72em; +} +.article-sort-title:hover:before { + border-color: var(--pseudo-hover); +} +.article-sort-title:before { + position: absolute; + top: calc(((100% - 36px) / 2)); + left: -9px; + z-index: 1; + width: 10px; + height: 10px; + border: 5px solid #49b1f5; + border-radius: 10px; + background: var(--card-bg); + content: ''; + line-height: 10px; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +.article-sort-title:after { + position: absolute; + bottom: 0; + left: 0; + z-index: 0; + width: 2px; + height: 1.5em; + background: #aadafa; + content: ''; +} +.article-sort-item { + position: relative; + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + margin: 0 0 20px 10px; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +.article-sort-item:hover:before { + border-color: var(--pseudo-hover); +} +.article-sort-item:before { + position: absolute; + left: calc(-20px - 17px); + width: 6px; + height: 6px; + border: 3px solid #49b1f5; + border-radius: 6px; + background: var(--card-bg); + content: ''; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +.article-sort-item.no-article-cover { + height: 80px; +} +.article-sort-item.no-article-cover .article-sort-item-info { + padding: 0; +} +.article-sort-item.year { + font-size: 1.43em; + margin-bottom: 10px; +} +.article-sort-item.year:hover:before { + border-color: #49b1f5; +} +.article-sort-item.year:before { + border-color: var(--pseudo-hover); +} +.article-sort-item-time { + color: var(--card-meta); + font-size: 0.85em; +} +.article-sort-item-time time { + padding-left: 6px; + cursor: default; +} +.article-sort-item-title { + color: var(--font-color); + font-size: 1.05em; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + -webkit-line-clamp: 2; +} +.article-sort-item-title:hover { + color: #49b1f5; + -webkit-transform: translateX(10px); + -moz-transform: translateX(10px); + -o-transform: translateX(10px); + -ms-transform: translateX(10px); + transform: translateX(10px); +} +.article-sort-item-img { + overflow: hidden; + width: 100px; + height: 70px; + border-radius: 6px; +} +@media screen and (max-width: 768px) { + .article-sort-item-img { + width: 70px; + height: 70px; + } +} +.article-sort-item-info { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + padding: 0 16px; +} +.category-lists .category-title { + font-size: 2.57em; +} +@media screen and (max-width: 768px) { + .category-lists .category-title { + font-size: 2em; + } +} +.category-lists .category-list { + margin-bottom: 0; +} +.category-lists .category-list a { + color: var(--font-color); +} +.category-lists .category-list a:hover { + color: #49b1f5; +} +.category-lists .category-list .category-list-count { + margin-left: 8px; + color: var(--card-meta); +} +.category-lists .category-list .category-list-count:before { + content: '('; +} +.category-lists .category-list .category-list-count:after { + content: ')'; +} +.category-lists ul { + padding: 0 0 0 20px; +} +.category-lists ul ul { + padding-left: 4px; +} +.category-lists ul li { + position: relative; + margin: 6px 0; + padding: 0.12em 0.4em 0.12em 1.4em; +} +#body-wrap { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -o-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + min-height: 100vh; +} +.layout { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1 auto; + -ms-flex: 1 auto; + flex: 1 auto; + margin: 0 auto; + padding: 40px 15px; + max-width: 1200px; + width: 100%; +} +@media screen and (max-width: 900px) { + .layout { + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -o-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } +} +@media screen and (max-width: 768px) { + .layout { + padding: 20px 5px; + } +} +@media screen and (min-width: 2000px) { + .layout { + max-width: 70%; + } +} +.layout > div:first-child:not(.nc) { + -webkit-align-self: flex-start; + align-self: flex-start; + -ms-flex-item-align: start; + padding: 50px 40px; +} +@media screen and (max-width: 768px) { + .layout > div:first-child:not(.nc) { + padding: 36px 14px; + } +} +.layout > div:first-child { + width: 74%; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +@media screen and (max-width: 900px) { + .layout > div:first-child { + width: 100% !important; + } +} +.layout.hide-aside { + max-width: 1000px; +} +@media screen and (min-width: 2000px) { + .layout.hide-aside { + max-width: 1300px; + } +} +.layout.hide-aside > div { + width: 100% !important; +} +.apple #page-header.full_page { + background-attachment: scroll !important; +} +.apple .recent-post-item, +.apple .avatar-img, +.apple .flink-item-icon { + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -o-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.container .flink { + margin-bottom: 20px; +} +.container .flink .flink-list { + overflow: auto; + padding: 10px 10px 0; + text-align: center; +} +.container .flink .flink-list > .flink-list-item { + position: relative; + float: left; + overflow: hidden; + margin: 15px 7px; + width: calc(100% / 3 - 15px); + height: 90px; + line-height: 17px; + -webkit-transform: translateZ(0); + border-radius: 8px; +} +@media screen and (max-width: 1024px) { + .container .flink .flink-list > .flink-list-item { + width: calc(50% - 15px) !important; + } +} +@media screen and (max-width: 600px) { + .container .flink .flink-list > .flink-list-item { + width: calc(100% - 15px) !important; + } +} +.container .flink .flink-list > .flink-list-item:hover .flink-item-icon { + margin-left: -10px; + width: 0; +} +.container .flink .flink-list > .flink-list-item:before { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; + background: var(--text-bg-hover); + content: ''; + -webkit-transition: -webkit-transform 0.3s ease-out; + -moz-transition: -moz-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + -ms-transition: -ms-transform 0.3s ease-out; + transition: transform 0.3s ease-out; + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); +} +.container .flink .flink-list > .flink-list-item:hover:before, +.container .flink .flink-list > .flink-list-item:focus:before, +.container .flink .flink-list > .flink-list-item:active:before { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); +} +.container .flink .flink-list > .flink-list-item a { + color: var(--font-color); + text-decoration: none; +} +.container .flink .flink-list > .flink-list-item a .flink-item-icon { + float: left; + overflow: hidden; + margin: 15px 10px; + width: 60px; + height: 60px; + border-radius: 7px; + -webkit-transition: width 0.3s ease-out; + -moz-transition: width 0.3s ease-out; + -o-transition: width 0.3s ease-out; + -ms-transition: width 0.3s ease-out; + transition: width 0.3s ease-out; +} +.container .flink .flink-list > .flink-list-item a .flink-item-icon img { + width: 100%; + height: 100%; + -webkit-transition: filter 375ms ease-in 0.2s, -webkit-transform 0.3s; + -moz-transition: filter 375ms ease-in 0.2s, -moz-transform 0.3s; + -o-transition: filter 375ms ease-in 0.2s, -o-transform 0.3s; + -ms-transition: filter 375ms ease-in 0.2s, -ms-transform 0.3s; + transition: filter 375ms ease-in 0.2s, transform 0.3s; + object-fit: cover; +} +.container .flink .flink-list > .flink-list-item a .img-alt { + display: none; +} +.container .flink .flink-item-name { + padding: 16px 10px 0 0; + height: 40px; + font-weight: bold; + font-size: 1.43em; +} +.container .flink .flink-item-desc { + padding: 16px 10px 16px 0; + height: 50px; + font-size: 0.93em; +} +.container .flink .flink-name { + margin-bottom: 5px; + font-weight: bold; + font-size: 1.5em; +} +#recent-posts .recent-post-item { + position: relative; + overflow: hidden; + margin-bottom: 20px; + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + -o-box-orient: horizontal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + height: 16.8em; +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item { + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -o-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: auto; + } +} +@media screen and (min-width: 2000px) { + #recent-posts .recent-post-item { + height: 18.8em; + } +} +#recent-posts .recent-post-item:hover .post-bg { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -o-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} +#recent-posts .recent-post-item.ads-wrap { + display: block !important; + height: auto !important; +} +#recent-posts .recent-post-item .post_cover { + overflow: hidden; + width: 42%; + height: 100%; +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item .post_cover { + width: 100%; + height: 230px; + } +} +#recent-posts .recent-post-item .post_cover.right { + -webkit-box-ordinal-group: 1; + -moz-box-ordinal-group: 1; + -o-box-ordinal-group: 1; + -ms-flex-order: 1; + -webkit-order: 1; + order: 1; +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item .post_cover.right { + -webkit-box-ordinal-group: 0; + -moz-box-ordinal-group: 0; + -o-box-ordinal-group: 0; + -ms-flex-order: 0; + -webkit-order: 0; + order: 0; + } +} +#recent-posts .recent-post-item .post_cover .post-bg { + z-index: -4; +} +#recent-posts .recent-post-item >.recent-post-info { + padding: 0 40px; + width: 58%; +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item >.recent-post-info { + padding: 20px 20px 30px; + width: 100%; + } +} +#recent-posts .recent-post-item >.recent-post-info.no-cover { + width: 100%; +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item >.recent-post-info.no-cover { + padding: 30px 20px; + } +} +#recent-posts .recent-post-item >.recent-post-info > .article-title { + color: var(--text-highlight-color); + font-size: 1.55em; + line-height: 1.4; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + -webkit-line-clamp: 2; +} +#recent-posts .recent-post-item >.recent-post-info > .article-title .sticky { + margin-right: 10px; + color: #ff7242; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} +@media screen and (max-width: 768px) { + #recent-posts .recent-post-item >.recent-post-info > .article-title { + font-size: 1.43em; + } +} +#recent-posts .recent-post-item >.recent-post-info > .article-title:hover { + color: #49b1f5; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap { + margin: 6px 0; + color: var(--card-meta); + font-size: 0.9em; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap > .post-meta-date { + cursor: default; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap i { + margin: 0 4px 0 0; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap .fa-spinner { + margin: 0; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap .article-meta-label { + padding-right: 4px; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap .article-meta-separator { + margin: 0 6px; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap .article-meta-link { + margin: 0 4px; +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap a { + color: var(--card-meta); +} +#recent-posts .recent-post-item >.recent-post-info > .article-meta-wrap a:hover { + color: #49b1f5; + text-decoration: underline; +} +#recent-posts .recent-post-item >.recent-post-info > .content { + -webkit-line-clamp: 2; +} +#article-container .shuoshuo-item { + margin-bottom: 20px; + padding: 35px 30px 30px; +} +@media screen and (max-width: 768px) { + #article-container .shuoshuo-item { + padding: 25px 20px 20px; + } +} +#article-container .shuoshuo-item-header { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + cursor: default; +} +#article-container .shuoshuo-avatar { + overflow: hidden; + width: 40px; + height: 40px; + border-radius: 40px; +} +#article-container .shuoshuo-avatar img { + margin: 0; + width: 100%; + height: 100%; +} +#article-container .shuoshuo-info { + margin-left: 10px; + line-height: 1.5; +} +#article-container .shuoshuo-date { + color: #858585; + font-size: 0.8em; +} +#article-container .shuoshuo-content { + padding: 15px 0 10px; +} +#article-container .shuoshuo-content > *:last-child { + margin-bottom: 0; +} +#article-container .shuoshuo-footer { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; +} +#article-container .shuoshuo-footer.flex-between { + -webkit-box-pack: justify; + -moz-box-pack: justify; + -o-box-pack: justify; + -ms-flex-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} +#article-container .shuoshuo-footer.flex-end { + -webkit-box-pack: end; + -moz-box-pack: end; + -o-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; +} +#article-container .shuoshuo-footer .shuoshuo-tag { + display: inline-block; + margin-right: 8px; + padding: 0 8px; + width: fit-content; + border: 1px solid #49b1f5; + border-radius: 12px; + color: #49b1f5; + font-size: 0.85em; + cursor: default; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +#article-container .shuoshuo-footer .shuoshuo-tag:hover { + background: #49b1f5; + color: var(--white); +} +#article-container .shuoshuo-footer .shuoshuo-comment-btn { + padding: 2px; + color: #90a4ae; + cursor: pointer; +} +#article-container .shuoshuo-footer .shuoshuo-comment-btn:hover { + color: #49b1f5; +} +#article-container .shuoshuo-comment { + padding-top: 10px; +} +#article-container .shuoshuo-comment.no-comment { + display: none; +} +.tag-cloud-list a { + display: inline-block; + margin: 2px; + padding: 2px 7px; + line-height: 1.7; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + border-radius: 5px; +} +.tag-cloud-list a:hover { + background: var(--btn-bg) !important; + -webkit-box-shadow: 2px 2px 6px rgba(0,0,0,0.2); + box-shadow: 2px 2px 6px rgba(0,0,0,0.2); + color: var(--btn-color) !important; +} +@media screen and (max-width: 768px) { + .tag-cloud-list a { + zoom: 0.85; + } +} +.tag-cloud-title { + font-size: 2.57em; +} +@media screen and (max-width: 768px) { + .tag-cloud-title { + font-size: 2em; + } +} +.page-title + .tag-cloud-list { + text-align: left; +} +#aside-content { + width: 26%; +} +@media screen and (min-width: 900px) { + #aside-content { + padding-left: 15px; + } +} +@media screen and (max-width: 900px) { + #aside-content { + margin-top: 20px; + width: 100%; + } +} +#aside-content .card-widget { + position: relative; + overflow: hidden; + margin-bottom: 20px; + padding: 20px 24px; +} +#aside-content .card-widget:last-child { + margin-bottom: 0; +} +#aside-content .card-info .author-info-name { + font-weight: 500; + font-size: 1.57em; +} +#aside-content .card-info .author-info-description { + margin-top: -0.42em; +} +#aside-content .card-info .site-data { + margin: 14px 0 4px; +} +#aside-content .card-info .card-info-social-icons { + margin: 6px 0 -6px; +} +#aside-content .card-info .card-info-social-icons .social-icon { + margin: 0 10px; + color: var(--font-color); + font-size: 1.4em; +} +#aside-content .card-info .card-info-social-icons i { + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +#aside-content .card-info .card-info-social-icons i:hover { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); +} +#aside-content .card-info #card-info-btn { + display: block; + margin-top: 14px; + background-color: var(--btn-bg); + color: var(--btn-color); + text-align: center; + line-height: 2.4; + border-radius: 7px; +} +#aside-content .card-info #card-info-btn:hover { + background-color: var(--btn-hover-color); +} +#aside-content .card-info #card-info-btn span { + padding-left: 10px; +} +#aside-content .item-headline { + padding-bottom: 6px; + font-size: 1.2em; +} +#aside-content .item-headline span { + margin-left: 6px; +} +@media screen and (min-width: 900px) { + #aside-content .sticky_layout { + position: sticky; + position: -webkit-sticky; + top: 20px; + -webkit-transition: top 0.3s; + -moz-transition: top 0.3s; + -o-transition: top 0.3s; + -ms-transition: top 0.3s; + transition: top 0.3s; + } +} +#aside-content .card-tag-cloud a { + display: inline-block; + padding: 0 4px; + line-height: 1.8; +} +#aside-content .card-tag-cloud a:hover { + color: #49b1f5 !important; +} +#aside-content .aside-list > span { + display: block; + margin-bottom: 10px; + text-align: center; +} +#aside-content .aside-list > .aside-list-item { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + padding: 6px 0; +} +#aside-content .aside-list > .aside-list-item:first-child { + padding-top: 0; +} +#aside-content .aside-list > .aside-list-item:not(:last-child) { + border-bottom: 1px dashed #f5f5f5; +} +#aside-content .aside-list > .aside-list-item:last-child { + padding-bottom: 0; +} +#aside-content .aside-list > .aside-list-item .thumbnail { + overflow: hidden; + width: 4em; + height: 4em; + border-radius: 6px; +} +#aside-content .aside-list > .aside-list-item .content { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + padding-left: 10px; + word-break: break-all; +} +#aside-content .aside-list > .aside-list-item .content > .name { + -webkit-line-clamp: 1; +} +#aside-content .aside-list > .aside-list-item .content > time, +#aside-content .aside-list > .aside-list-item .content > .name { + display: block; + color: var(--card-meta); + font-size: 0.85em; +} +#aside-content .aside-list > .aside-list-item .content > .title, +#aside-content .aside-list > .aside-list-item .content > .comment { + color: var(--font-color); + line-height: 1.5; + -webkit-line-clamp: 2; +} +#aside-content .aside-list > .aside-list-item .content > .title:hover, +#aside-content .aside-list > .aside-list-item .content > .comment:hover { + color: #49b1f5; +} +#aside-content .aside-list > .aside-list-item.no-cover { + min-height: 4.4em; +} +#aside-content .card-archives ul.card-archive-list, +#aside-content .card-categories ul.card-category-list { + margin: 0; + padding: 0; + list-style: none; +} +#aside-content .card-archives ul.card-archive-list > .card-archive-list-item a, +#aside-content .card-categories ul.card-category-list > .card-category-list-item a { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + -o-box-orient: horizontal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + margin: 2px 0; + padding: 2px 8px; + color: var(--font-color); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + border-radius: 6px; +} +#aside-content .card-archives ul.card-archive-list > .card-archive-list-item a:hover, +#aside-content .card-categories ul.card-category-list > .card-category-list-item a:hover { + padding: 2px 12px; + background-color: var(--text-bg-hover); + color: var(--white); +} +#aside-content .card-archives ul.card-archive-list > .card-archive-list-item a span:first-child, +#aside-content .card-categories ul.card-category-list > .card-category-list-item a span:first-child { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +#aside-content .card-categories .card-category-list.child { + padding: 0 0 0 16px; +} +#aside-content .card-categories .card-category-list > .parent > a.expand i { + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg); +} +#aside-content .card-categories .card-category-list > .parent > a.expand + .child { + display: block; +} +#aside-content .card-categories .card-category-list > .parent > a .card-category-list-name { + width: 70% !important; +} +#aside-content .card-categories .card-category-list > .parent > a .card-category-list-count { + width: calc(100% - 70% - 20px); + text-align: right; +} +#aside-content .card-categories .card-category-list > .parent > a i { + float: right; + margin-right: -0.5em; + padding: 0.5em; + -webkit-transition: -webkit-transform 0.3s; + -moz-transition: -moz-transform 0.3s; + -o-transition: -o-transform 0.3s; + -ms-transition: -ms-transform 0.3s; + transition: transform 0.3s; + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); +} +#aside-content .card-webinfo .webinfo .webinfo-item { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + padding: 2px 10px 0; +} +#aside-content .card-webinfo .webinfo .webinfo-item div:first-child { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + padding-right: 20px; +} +@media screen and (min-width: 901px) { + #aside-content #card-toc { + right: 0 !important; + } +} +@media screen and (max-width: 900px) { + #aside-content #card-toc { + position: fixed; + right: 55px; + bottom: 30px; + z-index: 100; + max-width: 380px; + max-height: calc(100% - 60px); + width: calc(100% - 80px); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + -ms-transition: none; + transition: none; + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + -webkit-transform-origin: right bottom; + -moz-transform-origin: right bottom; + -o-transform-origin: right bottom; + -ms-transform-origin: right bottom; + transform-origin: right bottom; + } + #aside-content #card-toc.open { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +#aside-content #card-toc .toc-percentage { + float: right; + margin-top: -9px; + color: #a9a9a9; + font-style: italic; + font-size: 140%; +} +#aside-content #card-toc .toc-content { + overflow-y: scroll; + overflow-y: overlay; + margin: 0 -24px; + max-height: calc(100vh - 120px); + width: calc(100% + 48px); +} +@media screen and (max-width: 900px) { + #aside-content #card-toc .toc-content { + max-height: calc(100vh - 140px); + } +} +#aside-content #card-toc .toc-content > * { + margin: 0 20px !important; +} +#aside-content #card-toc .toc-content > * > .toc-item > .toc-child { + margin-left: 10px; + padding-left: 10px; + border-left: 1px solid var(--dark-grey); +} +#aside-content #card-toc .toc-content:not(.is-expand) .toc-child { + display: none; +} +@media screen and (max-width: 900px) { + #aside-content #card-toc .toc-content:not(.is-expand) .toc-child { + display: block !important; + } +} +#aside-content #card-toc .toc-content:not(.is-expand) .toc-item.active .toc-child { + display: block; +} +#aside-content #card-toc .toc-content ol, +#aside-content #card-toc .toc-content li { + list-style: none; +} +#aside-content #card-toc .toc-content > ol { + padding: 0 !important; +} +#aside-content #card-toc .toc-content ol { + margin: 0; + padding-left: 18px; +} +#aside-content #card-toc .toc-content .toc-link { + display: block; + margin: 4px 0; + padding: 1px 8px; + color: var(--toc-link-color); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + border-radius: 6px; +} +#aside-content #card-toc .toc-content .toc-link:hover { + color: #49b1f5; +} +#aside-content #card-toc .toc-content .toc-link.active { + background: #00c4b6; + color: #fff; +} +#aside-content .sticky_layout:only-child > :first-child { + margin-top: 0; +} +#aside-content .card-more-btn { + float: right; + color: inherit; +} +#aside-content .card-more-btn:hover { + -webkit-animation: more-btn-move 1s infinite; + -moz-animation: more-btn-move 1s infinite; + -o-animation: more-btn-move 1s infinite; + -ms-animation: more-btn-move 1s infinite; + animation: more-btn-move 1s infinite; +} +#aside-content .card-announcement .item-headline i { + color: #f00; +} +.avatar-img { + overflow: hidden; + margin: 0 auto; + width: 110px; + height: 110px; + border-radius: 70px; +} +.avatar-img img { + width: 100%; + height: 100%; + -webkit-transition: filter 375ms ease-in 0.2s, -webkit-transform 0.3s; + -moz-transition: filter 375ms ease-in 0.2s, -moz-transform 0.3s; + -o-transition: filter 375ms ease-in 0.2s, -o-transform 0.3s; + -ms-transition: filter 375ms ease-in 0.2s, -ms-transform 0.3s; + transition: filter 375ms ease-in 0.2s, transform 0.3s; + object-fit: cover; +} +.avatar-img img:hover { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); +} +.site-data { + display: table; + width: 100%; + table-layout: fixed; +} +.site-data > a { + display: table-cell; +} +.site-data > a div { + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +.site-data > a:hover div { + color: #49b1f5 !important; +} +.site-data > a .headline { + color: var(--font-color); + font-size: 0.95em; +} +.site-data > a .length-num { + margin-top: -0.45em; + color: var(--text-highlight-color); + font-size: 1.2em; +} +@media screen and (min-width: 900px) { + html.hide-aside .layout { + -webkit-box-pack: center; + -moz-box-pack: center; + -o-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + } + html.hide-aside .layout > .aside-content { + display: none; + } + html.hide-aside .layout > div:first-child { + width: 80%; + } +} +.page .sticky_layout { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -o-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} +@-moz-keyframes more-btn-move { + 0%, 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(3px); + -moz-transform: translateX(3px); + -o-transform: translateX(3px); + -ms-transform: translateX(3px); + transform: translateX(3px); + } +} +@-webkit-keyframes more-btn-move { + 0%, 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(3px); + -moz-transform: translateX(3px); + -o-transform: translateX(3px); + -ms-transform: translateX(3px); + transform: translateX(3px); + } +} +@-o-keyframes more-btn-move { + 0%, 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(3px); + -moz-transform: translateX(3px); + -o-transform: translateX(3px); + -ms-transform: translateX(3px); + transform: translateX(3px); + } +} +@keyframes more-btn-move { + 0%, 100% { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -o-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(3px); + -moz-transform: translateX(3px); + -o-transform: translateX(3px); + -ms-transform: translateX(3px); + transform: translateX(3px); + } +} +@-moz-keyframes toc-open { + 0% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-webkit-keyframes toc-open { + 0% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-o-keyframes toc-open { + 0% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@keyframes toc-open { + 0% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} +@-moz-keyframes toc-close { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@-webkit-keyframes toc-close { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@-o-keyframes toc-close { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +@keyframes toc-close { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -o-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } + 100% { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -o-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + } +} +#post-comment .comment-head { + margin-bottom: 20px; +} +#post-comment .comment-head:after { + display: block; + clear: both; + content: ''; +} +#post-comment .comment-head .comment-headline { + display: inline-block; + vertical-align: middle; + font-weight: 700; + font-size: 1.43em; +} +#post-comment .comment-head .comment-switch { + display: inline-block; + float: right; + margin: 2px auto 0; + padding: 4px 16px; + width: max-content; + border-radius: 8px; + background: #f6f8fa; +} +#post-comment .comment-head .comment-switch .first-comment { + color: #49b1f5; +} +#post-comment .comment-head .comment-switch .second-comment { + color: #ff7242; +} +#post-comment .comment-head .comment-switch #switch-btn { + position: relative; + display: inline-block; + margin: -4px 8px 0; + width: 42px; + height: 22px; + border-radius: 34px; + background-color: #49b1f5; + vertical-align: middle; + cursor: pointer; + -webkit-transition: 0.4s; + -moz-transition: 0.4s; + -o-transition: 0.4s; + -ms-transition: 0.4s; + transition: 0.4s; +} +#post-comment .comment-head .comment-switch #switch-btn:before { + position: absolute; + bottom: 4px; + left: 4px; + width: 14px; + height: 14px; + border-radius: 50%; + background-color: #fff; + content: ''; + -webkit-transition: 0.4s; + -moz-transition: 0.4s; + -o-transition: 0.4s; + -ms-transition: 0.4s; + transition: 0.4s; +} +#post-comment .comment-wrap > div { + -webkit-animation: tabshow 0.5s; + -moz-animation: tabshow 0.5s; + -o-animation: tabshow 0.5s; + -ms-animation: tabshow 0.5s; + animation: tabshow 0.5s; +} +#post-comment .comment-wrap > div:nth-child(2) { + display: none; +} +#post-comment.move #switch-btn { + background-color: #ff7242; +} +#post-comment.move #switch-btn:before { + -webkit-transform: translateX(20px); + -moz-transform: translateX(20px); + -o-transform: translateX(20px); + -ms-transform: translateX(20px); + transform: translateX(20px); +} +#post-comment.move .comment-wrap > div:first-child { + display: none; +} +#post-comment.move .comment-wrap > div:last-child { + display: block; +} +#footer { + position: relative; + background-color: #49b1f5; + background-attachment: scroll; + background-position: bottom; + background-size: cover; +} +#footer-wrap { + position: relative; + padding: 40px 20px; + color: var(--light-grey); + text-align: center; +} +#footer-wrap a { + color: var(--light-grey); +} +#footer-wrap a:hover { + text-decoration: underline; +} +#footer-wrap .footer-separator { + margin: 0 4px; +} +#footer-wrap .icp-icon { + padding: 0 4px; + max-height: 1.4em; + width: auto; + vertical-align: text-bottom; +} +#page-header { + position: relative; + width: 100%; + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; +} +#page-header.full_page { + height: 100vh; + background-attachment: fixed; +} +#page-header.full_page #site-info { + position: absolute; + top: 43%; + padding: 0 10px; + width: 100%; +} +#page-header #site-title, +#page-header #site-subtitle, +#page-header #scroll-down .scroll-down-effects { + text-align: center; + text-shadow: 2px 2px 4px rgba(0,0,0,0.15); + line-height: 1.5; +} +#page-header #site-title { + margin: 0; + color: var(--white); + font-size: 1.85em; +} +@media screen and (min-width: 768px) { + #page-header #site-title { + font-size: 2.85em; + } +} +#page-header #site-subtitle { + color: var(--light-grey); + font-size: 1.15em; +} +@media screen and (min-width: 768px) { + #page-header #site-subtitle { + font-size: 1.72em; + } +} +#page-header #site_social_icons { + display: none; + margin: 0 auto; + text-align: center; +} +@media screen and (max-width: 768px) { + #page-header #site_social_icons { + display: block; + } +} +#page-header #site_social_icons .social-icon { + margin: 0 10px; + color: var(--light-grey); + text-shadow: 2px 2px 4px rgba(0,0,0,0.15); + font-size: 1.43em; +} +#page-header #scroll-down { + position: absolute; + bottom: 10px; + width: 100%; + cursor: pointer; +} +#page-header #scroll-down .scroll-down-effects { + position: relative; + width: 100%; + color: var(--light-grey); + font-size: 20px; +} +#page-header.not-home-page { + height: 400px; +} +@media screen and (max-width: 768px) { + #page-header.not-home-page { + height: 280px; + } +} +#page-header #page-site-info { + position: absolute; + top: 200px; + padding: 0 10px; + width: 100%; +} +@media screen and (max-width: 768px) { + #page-header #page-site-info { + top: 140px; + } +} +#page-header.post-bg { + height: 400px; +} +@media screen and (max-width: 768px) { + #page-header.post-bg { + height: 360px; + } +} +#page-header #post-info { + position: absolute; + width: 100%; + bottom: 30px; +} +#page-header #post-info > * { + margin: 0 auto; + padding: 0 15px; + max-width: 1200px; +} +@media screen and (min-width: 768px) and (max-width: 1300px) { + #page-header #post-info > * { + padding: 0 30px; + } +} +@media screen and (min-width: 2000px) { + #page-header #post-info > * { + max-width: 70%; + } +} +#page-header.not-top-img { + margin-bottom: 10px; + height: 60px; + background: 0; +} +#page-header.not-top-img .title-seo { + display: none; +} +#page-header.not-top-img #nav { + background: rgba(255,255,255,0.8); + -webkit-box-shadow: 0 5px 6px -5px rgba(133,133,133,0.6); + box-shadow: 0 5px 6px -5px rgba(133,133,133,0.6); +} +#page-header.not-top-img #nav a, +#page-header.not-top-img #nav span.site-page, +#page-header.not-top-img #nav .site-name { + color: var(--font-color); + text-shadow: none; +} +#page-header.nav-fixed #nav { + position: fixed; + top: -60px; + z-index: 91; + background: rgba(255,255,255,0.7); + -webkit-box-shadow: 0 5px 6px -5px rgba(133,133,133,0.6); + box-shadow: 0 5px 6px -5px rgba(133,133,133,0.6); + -webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in-out; + -moz-transition: -moz-transform 0.2s ease-in-out, opacity 0.2s ease-in-out; + -o-transition: -o-transform 0.2s ease-in-out, opacity 0.2s ease-in-out; + -ms-transition: -ms-transform 0.2s ease-in-out, opacity 0.2s ease-in-out; + transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out; + will-change: transform; + backdrop-filter: blur(7px); +} +#page-header.nav-fixed #nav #blog-info { + color: var(--font-color); +} +#page-header.nav-fixed #nav #blog-info:hover { + color: #49b1f5; +} +#page-header.nav-fixed #nav #blog-info .site-name { + text-shadow: none; +} +#page-header.nav-fixed #nav #blog-info > a:first-child { + display: none; +} +#page-header.nav-fixed #nav #blog-info > a:last-child { + display: inline; +} +#page-header.nav-fixed #nav a, +#page-header.nav-fixed #nav span.site-page, +#page-header.nav-fixed #nav #toggle-menu { + color: var(--font-color); + text-shadow: none; +} +#page-header.nav-fixed #nav a:hover, +#page-header.nav-fixed #nav span.site-page:hover, +#page-header.nav-fixed #nav #toggle-menu:hover { + color: #49b1f5; +} +#page-header.nav-fixed.fixed #nav { + top: 0; + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; +} +#page-header.nav-visible:not(.fixed) #nav { + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; + -webkit-transform: translate3d(0, 100%, 0); + -moz-transform: translate3d(0, 100%, 0); + -o-transform: translate3d(0, 100%, 0); + -ms-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); +} +#page-header.nav-visible:not(.fixed) + .layout > .aside-content > .sticky_layout { + top: 70px; + -webkit-transition: top 0.5s; + -moz-transition: top 0.5s; + -o-transition: top 0.5s; + -ms-transition: top 0.5s; + transition: top 0.5s; +} +#page-header.fixed #nav { + position: fixed; +} +#page-header.fixed + .layout > .aside-content > .sticky_layout { + top: 70px; + -webkit-transition: top 0.5s; + -moz-transition: top 0.5s; + -o-transition: top 0.5s; + -ms-transition: top 0.5s; + transition: top 0.5s; +} +#page-header.fixed + .layout #card-toc .toc-content { + max-height: calc(100vh - 170px); +} +#page .page-title { + margin: 0 0 10px; + font-weight: bold; + font-size: 2em; +} +#post > #post-info { + margin-bottom: 30px; +} +#post > #post-info .post-title { + padding-bottom: 4px; + border-bottom: 1px solid var(--light-grey); + color: var(--text-highlight-color); +} +#post > #post-info .post-title .post-edit-link { + float: right; +} +#post > #post-info #post-meta, +#post > #post-info #post-meta a { + color: #78818a; +} +#post-info .post-title { + margin-bottom: 8px; + color: var(--white); + font-weight: normal; + font-size: 2.5em; + line-height: 1.5; + -webkit-line-clamp: 3; +} +@media screen and (max-width: 768px) { + #post-info .post-title { + font-size: 2.1em; + } +} +#post-info .post-title .post-edit-link { + padding-left: 10px; +} +#post-info #post-meta { + color: var(--light-grey); + font-size: 95%; +} +@media screen and (min-width: 768px) { + #post-info #post-meta > .meta-secondline > span:first-child { + display: none; + } +} +@media screen and (max-width: 768px) { + #post-info #post-meta { + font-size: 90%; + } + #post-info #post-meta > .meta-firstline, + #post-info #post-meta > .meta-secondline { + display: inline; + } +} +#post-info #post-meta .post-meta-separator { + margin: 0 5px; +} +#post-info #post-meta .post-meta-icon { + margin-right: 4px; +} +#post-info #post-meta .post-meta-label { + margin-right: 4px; +} +#post-info #post-meta a { + color: var(--light-grey); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} +#post-info #post-meta a:hover { + color: #49b1f5; + text-decoration: underline; +} +#nav { + position: absolute; + top: 0; + z-index: 90; + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -o-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + padding: 0 36px; + width: 100%; + height: 60px; + font-size: 1.3em; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; +} +@media screen and (max-width: 768px) { + #nav { + padding: 0 16px; + } +} +#nav.show { + opacity: 1; + -ms-filter: none; + filter: none; +} +#nav #blog-info { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + color: var(--light-grey); +} +#nav #blog-info .site-icon { + margin-right: 6px; + height: 36px; + vertical-align: middle; +} +#nav #blog-info .nav-page-title { + display: none; +} +#nav #toggle-menu { + display: none; + padding: 2px 0 0 6px; + vertical-align: top; +} +#nav #toggle-menu:hover { + color: var(--white); +} +#nav a, +#nav span.site-page { + color: var(--light-grey); +} +#nav a:hover, +#nav span.site-page:hover { + color: var(--white); +} +#nav .site-name { + text-shadow: 2px 2px 4px rgba(0,0,0,0.15); + font-weight: bold; +} +#nav .menus_items { + display: inline; +} +#nav .menus_items .menus_item { + position: relative; + display: inline-block; + padding: 0 0 0 14px; +} +#nav .menus_items .menus_item:hover .menus_item_child { + display: block; +} +#nav .menus_items .menus_item:hover > span > i:last-child { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -o-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +#nav .menus_items .menus_item > span > i:last-child { + padding: 4px; + -webkit-transition: -webkit-transform 0.3s; + -moz-transition: -moz-transform 0.3s; + -o-transition: -o-transform 0.3s; + -ms-transition: -ms-transform 0.3s; + transition: transform 0.3s; +} +#nav .menus_items .menus_item .menus_item_child { + position: absolute; + right: 0; + display: none; + margin-top: 8px; + padding: 0; + width: max-content; + background-color: var(--sidebar-bg); + -webkit-box-shadow: 0 5px 20px -4px rgba(0,0,0,0.5); + box-shadow: 0 5px 20px -4px rgba(0,0,0,0.5); + -webkit-animation: sub_menus 0.3s 0.1s ease both; + -moz-animation: sub_menus 0.3s 0.1s ease both; + -o-animation: sub_menus 0.3s 0.1s ease both; + -ms-animation: sub_menus 0.3s 0.1s ease both; + animation: sub_menus 0.3s 0.1s ease both; + border-radius: 5px; +} +#nav .menus_items .menus_item .menus_item_child:before { + position: absolute; + top: -8px; + left: 0; + width: 100%; + height: 20px; + content: ''; +} +#nav .menus_items .menus_item .menus_item_child li { + list-style: none; +} +#nav .menus_items .menus_item .menus_item_child li:hover { + background: var(--text-bg-hover); +} +#nav .menus_items .menus_item .menus_item_child li:first-child { + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +#nav .menus_items .menus_item .menus_item_child li:last-child { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} +#nav .menus_items .menus_item .menus_item_child li a { + display: inline-block; + padding: 8px 16px; + width: 100%; + color: var(--font-color) !important; + text-shadow: none !important; +} +#nav.hide-menu #toggle-menu { + display: inline-block !important; +} +#nav.hide-menu #toggle-menu .site-page { + font-size: inherit; +} +#nav.hide-menu .menus_items { + display: none; +} +#nav.hide-menu #search-button span:not(.site-page) { + display: none; +} +#nav #search-button { + display: inline; + padding: 0 0 0 14px; +} +#nav .site-page { + position: relative; + padding-bottom: 6px; + text-shadow: 1px 1px 2px rgba(0,0,0,0.3); + font-size: 0.78em; + cursor: pointer; +} +#nav .site-page:not(.child):after { + position: absolute; + bottom: 0; + left: 0; + z-index: -1; + width: 0; + height: 3px; + background-color: #80c8f8; + content: ''; + -webkit-transition: all 0.3s ease-in-out; + -moz-transition: all 0.3s ease-in-out; + -o-transition: all 0.3s ease-in-out; + -ms-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + border-radius: 6px; +} +#nav .site-page:not(.child):hover:after { + width: 100%; +} +#pagination .pagination { + margin-top: 20px; + text-align: center; +} +#pagination .page-number.current { + background: #00c4b6; + color: var(--white); +} +#pagination .full-width { + width: 100% !important; +} +#pagination .pagination-related { + width: 50%; + height: 150px; +} +@media screen and (max-width: 768px) { + #pagination .pagination-related { + width: 100%; + } +} +#pagination .pagination-related .info-1 .info-item-2 { + -webkit-line-clamp: 1; +} +#pagination .pagination-related .info-2 .info-item-1 { + -webkit-line-clamp: 2; +} +#pagination.pagination-post { + overflow: hidden; + margin-top: 40px; + width: 100%; + border-radius: 6px; +} +.layout .pagination > * { + display: inline-block; + margin: 0 6px; + width: 2.5em; + height: 2.5em; + line-height: 2.5em; +} +.layout .pagination > *:not(.space):hover { + background: var(--btn-hover-color); + color: var(--btn-color); +} +#archive .pagination { + margin-top: 30px; +} +#archive .pagination > *:not(.space) { + -webkit-box-shadow: none; + box-shadow: none; +} +.pagination-related { + position: relative; + display: inline-block; + overflow: hidden; + background: #000; + vertical-align: bottom; +} +.pagination-related.next-post .info { + text-align: right; +} +.pagination-related .info .info-1, +.pagination-related .info .info-2 { + padding: 20px 40px; + color: var(--white); + -webkit-transition: -webkit-transform 0.3s, opacity 0.3s; + -moz-transition: -moz-transform 0.3s, opacity 0.3s; + -o-transition: -o-transform 0.3s, opacity 0.3s; + -ms-transition: -ms-transform 0.3s, opacity 0.3s; + transition: transform 0.3s, opacity 0.3s; +} +.pagination-related .info .info-1 .info-item-1 { + color: var(--light-grey); + text-transform: uppercase; + font-size: 90%; +} +.pagination-related .info .info-1 .info-item-2 { + color: var(--white); + font-weight: 500; +} +.pagination-related .info .info-2 { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.pagination-related:not(.no-desc):hover .info-1 { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transform: translate(0, -100%); + -moz-transform: translate(0, -100%); + -o-transform: translate(0, -100%); + -ms-transform: translate(0, -100%); + transform: translate(0, -100%); +} +.pagination-related:not(.no-desc):hover .info-2 { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, -50%); + -moz-transform: translate(0, -50%); + -o-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); +} +.container { + word-wrap: break-word; + overflow-wrap: break-word; +} +.container a { + color: #49b1f5; +} +.container a:hover { + text-decoration: underline; +} +.container img { + display: block; + margin: 0 auto 20px; + max-width: 100%; + -webkit-transition: filter 375ms ease-in 0.2s; + -moz-transition: filter 375ms ease-in 0.2s; + -o-transition: filter 375ms ease-in 0.2s; + -ms-transition: filter 375ms ease-in 0.2s; + transition: filter 375ms ease-in 0.2s; + border-radius: 6px; +} +.container p { + margin: 0 0 16px; +} +.container iframe { + margin: 0 0 20px; +} +.container kbd { + margin: 0 3px; + padding: 3px 5px; + border: 1px solid #b4b4b4; + background-color: #f8f8f8; + -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.25), 0 2px 1px 0 rgba(255,255,255,0.6) inset; + box-shadow: 0 1px 3px rgba(0,0,0,0.25), 0 2px 1px 0 rgba(255,255,255,0.6) inset; + color: #34495e; + white-space: nowrap; + font-weight: 600; + font-size: 0.9em; + font-family: Monaco, 'Ubuntu Mono', monospace; + line-height: 1em; + border-radius: 3px; +} +.container ol ol, +.container ul ol, +.container ol ul, +.container ul ul { + padding-left: 20px; +} +.container ol li, +.container ul li { + margin: 4px 0; +} +.container ol p, +.container ul p { + margin: 0 0 8px; +} +.container > :last-child { + margin-bottom: 0 !important; +} +.container hr { + margin: 20px 0; +} +#post .tag_share:after { + display: block; + clear: both; + content: ''; +} +#post .tag_share .post-meta__tag-list { + display: inline-block; +} +#post .tag_share .post-meta__tags { + display: inline-block; + margin: 8px 8px 8px 0; + padding: 0 12px; + width: fit-content; + border: 1px solid #49b1f5; + border-radius: 12px; + color: #49b1f5; + font-size: 0.85em; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -ms-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +#post .tag_share .post-meta__tags:hover { + background: #49b1f5; + color: var(--white); +} +#post .tag_share .post-share { + display: inline-block; + float: right; + margin: 8px 0 0; + width: fit-content; +} +#post .tag_share .post-share .social-share { + font-size: 0.85em; +} +#post .tag_share .post-share .social-share .social-share-icon { + margin: 0 4px; + width: 1.85em; + height: 1.85em; + font-size: 1.2em; + line-height: 1.85em; +} +#post .post-copyright { + position: relative; + margin: 40px 0 10px; + padding: 10px 16px; + border: 1px solid var(--light-grey); + -webkit-transition: box-shadow 0.3s ease-in-out; + -moz-transition: box-shadow 0.3s ease-in-out; + -o-transition: box-shadow 0.3s ease-in-out; + -ms-transition: box-shadow 0.3s ease-in-out; + transition: box-shadow 0.3s ease-in-out; + border-radius: 6px; +} +#post .post-copyright:before { + position: absolute; + top: 2px; + right: 12px; + color: #49b1f5; + content: '\f1f9'; + font-size: 1.3em; +} +#post .post-copyright:hover { + -webkit-box-shadow: 0 0 8px 0 rgba(232,237,250,0.6), 0 2px 4px 0 rgba(232,237,250,0.5); + box-shadow: 0 0 8px 0 rgba(232,237,250,0.6), 0 2px 4px 0 rgba(232,237,250,0.5); +} +#post .post-copyright .post-copyright-meta { + color: #49b1f5; + font-weight: bold; +} +#post .post-copyright .post-copyright-meta i { + margin-right: 3px; +} +#post .post-copyright .post-copyright-info { + padding-left: 6px; +} +#post .post-copyright .post-copyright-info a { + text-decoration: underline; + word-break: break-word; +} +#post .post-copyright .post-copyright-info a:hover { + text-decoration: none; +} +#post #post-outdate-notice { + position: relative; + margin: 0 0 20px; + padding: 0.5em 1.2em; + background-color: #ffe6e6; + color: #f66; + border-radius: 3px; + padding: 0.5em 1em 0.5em 2.6em; + border-left: 5px solid #ff8080; +} +#post #post-outdate-notice .num { + padding: 0 4px; +} +#post #post-outdate-notice:before { + position: absolute; + top: 50%; + left: 0.9em; + color: #ff8080; + content: '\f071'; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -o-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} +#post .ads-wrap { + margin: 40px 0; +} +.relatedPosts { + margin-top: 40px; +} +.relatedPosts > .headline { + margin-bottom: 5px; + font-weight: 700; + font-size: 1.43em; +} +.relatedPosts > .relatedPosts-list > a { + margin: 3px; + width: calc(33.333% - 6px); + height: 200px; + border-radius: 6px; +} +@media screen and (max-width: 768px) { + .relatedPosts > .relatedPosts-list > a { + margin: 2px; + width: calc(50% - 4px); + height: 150px; + } +} +@media screen and (max-width: 600px) { + .relatedPosts > .relatedPosts-list > a { + width: calc(100% - 4px); + } +} +.relatedPosts > .relatedPosts-list .info .info-1 .info-item-2 { + -webkit-line-clamp: 2; +} +.relatedPosts > .relatedPosts-list .info .info-2 .info-item-1 { + -webkit-line-clamp: 3; +} +.post-reward { + position: relative; + margin-top: 80px; + width: 100%; + text-align: center; + pointer-events: none; +} +.post-reward > * { + pointer-events: auto; +} +.post-reward .reward-button { + display: inline-block; + padding: 4px 24px; + background: var(--btn-bg); + color: var(--btn-color); + cursor: pointer; + border-radius: 6px; +} +.post-reward .reward-button i { + margin-right: 5px; +} +.post-reward:hover .reward-button { + background: var(--btn-hover-color); +} +.post-reward:hover > .reward-main { + display: block; +} +.post-reward .reward-main { + position: absolute; + bottom: 40px; + left: 0; + z-index: 100; + display: none; + padding: 0 0 15px; + width: 100%; + border-radius: 6px; +} +.post-reward .reward-main .reward-all { + display: inline-block; + margin: 0; + padding: 20px 10px; + background: var(--reward-pop); +} +.post-reward .reward-main .reward-all:before { + position: absolute; + bottom: -10px; + left: 0; + width: 100%; + height: 20px; + content: ''; +} +.post-reward .reward-main .reward-all:after { + position: absolute; + right: 0; + bottom: 2px; + left: 0; + margin: 0 auto; + width: 0; + height: 0; + border-top: 13px solid var(--reward-pop); + border-right: 13px solid transparent; + border-left: 13px solid transparent; + content: ''; +} +.post-reward .reward-main .reward-all .reward-item { + display: inline-block; + padding: 0 8px; + list-style-type: none; + vertical-align: top; +} +.post-reward .reward-main .reward-all .reward-item img { + width: 130px; + height: 130px; +} +.post-reward .reward-main .reward-all .reward-item .post-qr-code-desc { + width: 130px; + color: #858585; +} +#rightside { + position: fixed; + right: -48px; + bottom: 40px; + z-index: 100; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; +} +#rightside.rightside-show { + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); + -webkit-transform: translate(-58px, 0); + -moz-transform: translate(-58px, 0); + -o-transform: translate(-58px, 0); + -ms-transform: translate(-58px, 0); + transform: translate(-58px, 0); +} +#rightside #rightside-config-hide { + height: 0; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: -webkit-transform 0.4s; + -moz-transition: -moz-transform 0.4s; + -o-transition: -o-transform 0.4s; + -ms-transition: -ms-transform 0.4s; + transition: transform 0.4s; + -webkit-transform: translate(45px, 0); + -moz-transform: translate(45px, 0); + -o-transform: translate(45px, 0); + -ms-transform: translate(45px, 0); + transform: translate(45px, 0); +} +#rightside #rightside-config-hide.show { + height: auto; + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -o-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +#rightside #rightside-config-hide.status { + height: auto; + opacity: 1; + -ms-filter: none; + filter: none; +} +#rightside > div > button, +#rightside > div > a { + display: block; + margin-bottom: 5px; + width: 35px; + height: 35px; + background-color: var(--btn-bg); + color: var(--btn-color); + text-align: center; + font-size: 16px; + line-height: 35px; + border-radius: 5px; +} +#rightside > div > button:hover, +#rightside > div > a:hover { + background-color: var(--btn-hover-color); +} +#rightside #mobile-toc-button { + display: none; +} +@media screen and (max-width: 900px) { + #rightside #mobile-toc-button { + display: block; + } +} +@media screen and (max-width: 900px) { + #rightside #hide-aside-btn { + display: none; + } +} +#sidebar #menu-mask { + position: fixed; + z-index: 102; + display: none; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.8); +} +#sidebar #sidebar-menus { + position: fixed; + top: 0; + right: -330px; + z-index: 103; + overflow-x: hidden; + overflow-y: scroll; + padding-left: 5px; + width: 330px; + height: 100%; + background: var(--sidebar-bg); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + -ms-transition: all 0.5s; + transition: all 0.5s; +} +#sidebar #sidebar-menus.open { + -webkit-transform: translate3d(-100%, 0, 0); + -moz-transform: translate3d(-100%, 0, 0); + -o-transform: translate3d(-100%, 0, 0); + -ms-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); +} +#sidebar #sidebar-menus > .avatar-img { + margin: 20px auto; +} +#sidebar #sidebar-menus .site-data { + padding: 0 10px; +} +#sidebar #sidebar-menus hr { + margin: 20px auto; +} +#sidebar #sidebar-menus .menus_items { + margin: 20px; + padding: 15px; + background: var(--sidebar-menu-bg); + -webkit-box-shadow: 0 0 1px 1px rgba(7,17,27,0.05); + box-shadow: 0 0 1px 1px rgba(7,17,27,0.05); + border-radius: 10px; +} +#sidebar #sidebar-menus .menus_items .site-page { + position: relative; + display: block; + margin: 4px 0; + padding: 2px 23px 2px 15px; + color: var(--font-color); + font-size: 1.15em; + cursor: pointer; + border-radius: 6px; +} +#sidebar #sidebar-menus .menus_items .site-page:hover { + background: var(--text-bg-hover); + color: var(--white); +} +#sidebar #sidebar-menus .menus_items .site-page i:first-child { + width: 15%; + text-align: left; +} +#sidebar #sidebar-menus .menus_items .site-page.group > i:last-child { + position: absolute; + top: 0.6em; + right: 10px; + -webkit-transition: -webkit-transform 0.3s; + -moz-transition: -moz-transform 0.3s; + -o-transition: -o-transform 0.3s; + -ms-transition: -ms-transform 0.3s; + transition: transform 0.3s; +} +#sidebar #sidebar-menus .menus_items .site-page.group.hide > i:last-child { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +#sidebar #sidebar-menus .menus_items .site-page.group.hide + .menus_item_child { + display: none; +} +#sidebar #sidebar-menus .menus_items .menus_item_child { + margin: 0; + padding-left: 25px; + list-style: none; +} +#vcomment { + font-size: 1.1em; +} +#vcomment .vbtn { + border: none; + background: var(--btn-bg); + color: var(--btn-color); +} +#vcomment .vbtn:hover { + background: var(--btn-hover-color); +} +#vcomment .vimg { + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +#vcomment .vimg:hover { + -webkit-transform: rotate(360deg); + -moz-transform: rotate(360deg); + -o-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); +} +#vcomment .vcards .vcard .vcontent.expand:before, +#vcomment .vcards .vcard .vcontent.expand:after { + z-index: 22; +} +#waline-wrap { + --waline-font-size: 1.1em; + --waline-theme-color: #49b1f5; + --waline-active-color: #ff7242; +} +#waline-wrap .wl-comment-actions > button:not(last-child) { + padding-right: 4px; +} +.twikoo .tk-content p { + margin: 3px 0; +} +.fireworks { + position: fixed; + top: 0; + left: 0; + z-index: 9999; + pointer-events: none; +} +.medium-zoom-image--opened { + z-index: 99999 !important; + margin: 0 !important; +} +.medium-zoom-overlay { + z-index: 99999 !important; +} +.utterances, +.fb-comments iframe { + width: 100% !important; +} +#gitalk-container .gt-meta { + margin: 0 0 0.8em; + padding: 6px 0 16px; +} +.aplayer { + color: #4c4948; +} +.container .aplayer { + margin: 0 0 20px; +} +.snackbar-container.snackbar-css { + border-radius: 5px; + opacity: 0.85 !important; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)" !important; + filter: alpha(opacity=85) !important; +} +.abc-music-sheet { + margin: 0 0 20px; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: opacity 0.3s; + -moz-transition: opacity 0.3s; + -o-transition: opacity 0.3s; + -ms-transition: opacity 0.3s; + transition: opacity 0.3s; +} +.abc-music-sheet.abcjs-container { + opacity: 1; + -ms-filter: none; + filter: none; +} +@media screen and (max-width: 768px) { + .fancybox__toolbar__column.is-middle { + display: none; + } +} +.container .btn-center { + margin: 0 0 20px; + text-align: center; +} +.container .btn-beautify { + display: inline-block; + margin: 0 4px 6px; + padding: 0 15px; + background-color: var(--btn-beautify-color, #777); + color: #fff; + line-height: 2; + border-radius: 6px; +} +.container .btn-beautify.blue { + --btn-beautify-color: #428bca; +} +.container .btn-beautify.pink { + --btn-beautify-color: #ff69b4; +} +.container .btn-beautify.red { + --btn-beautify-color: #f00; +} +.container .btn-beautify.purple { + --btn-beautify-color: #6f42c1; +} +.container .btn-beautify.orange { + --btn-beautify-color: #ff8c00; +} +.container .btn-beautify.green { + --btn-beautify-color: #5cb85c; +} +.container .btn-beautify:hover { + background-color: var(--btn-hover-color); +} +.container .btn-beautify i + span { + margin-left: 6px; +} +.container .btn-beautify:not(.block) + .btn-beautify:not(.block) { + margin: 0 4px 20px; +} +.container .btn-beautify.block { + display: block; + margin: 0 0 20px; + width: fit-content; + width: -moz-fit-content; +} +.container .btn-beautify.block.center { + margin: 0 auto 20px; +} +.container .btn-beautify.block.right { + margin: 0 0 20px auto; +} +.container .btn-beautify.larger { + padding: 6px 15px; +} +.container .btn-beautify:hover { + text-decoration: none; +} +.container .btn-beautify.outline { + border: 1px solid transparent; + border-color: var(--btn-beautify-color, #777); + background-color: transparent; + color: var(--btn-beautify-color, #777); +} +.container .btn-beautify.outline:hover { + background-color: var(--btn-beautify-color, #777); +} +.container .btn-beautify.outline:hover { + color: #fff !important; +} +.container figure.gallery-group { + position: relative; + float: left; + overflow: hidden; + margin: 6px 4px; + width: calc(50% - 8px); + height: 250px; + border-radius: 10px; + background: #000; + -webkit-transform: translate3d(0, 0, 0); +} +@media screen and (max-width: 600px) { + .container figure.gallery-group { + width: calc(100% - 8px); + } +} +@media screen and (min-width: 1024px) { + .container figure.gallery-group { + width: calc(100% / 3 - 8px); + } +} +.container figure.gallery-group:hover img { + opacity: 0.4; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; + filter: alpha(opacity=40); + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.container figure.gallery-group:hover .gallery-group-name::after { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.container figure.gallery-group:hover p { + opacity: 1; + -ms-filter: none; + filter: none; + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.container figure.gallery-group img { + position: relative; + margin: 0; + max-width: none; + width: calc(100% + 20px); + height: 250px; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); + -webkit-transition: all 0.3s, filter 375ms ease-in 0.2s; + -moz-transition: all 0.3s, filter 375ms ease-in 0.2s; + -o-transition: all 0.3s, filter 375ms ease-in 0.2s; + -ms-transition: all 0.3s, filter 375ms ease-in 0.2s; + transition: all 0.3s, filter 375ms ease-in 0.2s; + -webkit-transform: translate3d(-10px, 0, 0); + -moz-transform: translate3d(-10px, 0, 0); + -o-transform: translate3d(-10px, 0, 0); + -ms-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + object-fit: cover; +} +.container figure.gallery-group figcaption { + position: absolute; + top: 0; + left: 0; + padding: 30px; + width: 100%; + height: 100%; + color: #fff; + text-transform: uppercase; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; +} +.container figure.gallery-group figcaption > a { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1000; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); +} +.container figure.gallery-group p { + margin: 0; + padding: 8px 0 0; + letter-spacing: 1px; + font-size: 1.1em; + line-height: 1.5; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: opacity 0.35s, -webkit-transform 0.35s; + -moz-transition: opacity 0.35s, -moz-transform 0.35s; + -o-transition: opacity 0.35s, -o-transform 0.35s; + -ms-transition: opacity 0.35s, -ms-transform 0.35s; + transition: opacity 0.35s, transform 0.35s; + -webkit-transform: translate3d(100%, 0, 0); + -moz-transform: translate3d(100%, 0, 0); + -o-transform: translate3d(100%, 0, 0); + -ms-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + -webkit-line-clamp: 4; +} +.container figure.gallery-group .gallery-group-name { + position: relative; + margin: 0; + padding: 8px 0; + font-weight: bold; + font-size: 1.65em; + line-height: 1.5; + -webkit-line-clamp: 2; +} +.container figure.gallery-group .gallery-group-name:after { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 2px; + background: #fff; + content: ''; + -webkit-transition: -webkit-transform 0.35s; + -moz-transition: -moz-transform 0.35s; + -o-transition: -o-transform 0.35s; + -ms-transition: -ms-transform 0.35s; + transition: transform 0.35s; + -webkit-transform: translate3d(-100%, 0, 0); + -moz-transform: translate3d(-100%, 0, 0); + -o-transform: translate3d(-100%, 0, 0); + -ms-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); +} +.container .gallery-group-main { + overflow: auto; + padding: 0 0 16px; +} +.container .gallery-container { + margin: 0 0 20px; + text-align: center; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); +} +.container .gallery-container.loaded { + opacity: 1; + -ms-filter: none; + filter: none; +} +.container .gallery-container img { + display: initial; + margin: 0; + width: 100%; + height: 100%; +} +.container .gallery-container .gallery-data { + display: none; +} +.container .gallery-container button { + margin-top: 25px; + padding: 8px 14px; + background: var(--btn-bg); + color: var(--btn-color); + font-weight: bold; + font-size: 1.1em; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + border-radius: 5px; +} +.container .gallery-container button:hover { + background: var(--btn-hover-color); +} +.container .gallery-container button:hover i { + margin-left: 8px; +} +.container .gallery-container button i { + margin-left: 4px; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; +} +.container .loading-container { + display: inline-block; + overflow: hidden; + width: 154px; + height: 154px; +} +.container .loading-container .loading-item { + position: relative; + width: 100%; + height: 100%; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform: translateZ(0) scale(1); + -moz-transform: translateZ(0) scale(1); + -o-transform: translateZ(0) scale(1); + -ms-transform: translateZ(0) scale(1); + transform: translateZ(0) scale(1); + -webkit-transform-origin: 0 0; + -moz-transform-origin: 0 0; + -o-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; +} +.container .loading-container .loading-item div { + position: absolute; + width: 30.8px; + height: 30.8px; + border-radius: 50%; + background: #e15b64; + -webkit-transform: translate(61.6px, 61.6px) scale(1); + -moz-transform: translate(61.6px, 61.6px) scale(1); + -o-transform: translate(61.6px, 61.6px) scale(1); + -ms-transform: translate(61.6px, 61.6px) scale(1); + transform: translate(61.6px, 61.6px) scale(1); + -webkit-animation: loading-ball 1.92s infinite cubic-bezier(0, 0.5, 0.5, 1); + -moz-animation: loading-ball 1.92s infinite cubic-bezier(0, 0.5, 0.5, 1); + -o-animation: loading-ball 1.92s infinite cubic-bezier(0, 0.5, 0.5, 1); + -ms-animation: loading-ball 1.92s infinite cubic-bezier(0, 0.5, 0.5, 1); + animation: loading-ball 1.92s infinite cubic-bezier(0, 0.5, 0.5, 1); +} +.container .loading-container .loading-item div:nth-child(1) { + background: #f47e60; + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + -webkit-animation: loading-ball-r 0.48s infinite cubic-bezier(0, 0.5, 0.5, 1), loading-ball-c 1.92s infinite step-start; + -moz-animation: loading-ball-r 0.48s infinite cubic-bezier(0, 0.5, 0.5, 1), loading-ball-c 1.92s infinite step-start; + -o-animation: loading-ball-r 0.48s infinite cubic-bezier(0, 0.5, 0.5, 1), loading-ball-c 1.92s infinite step-start; + -ms-animation: loading-ball-r 0.48s infinite cubic-bezier(0, 0.5, 0.5, 1), loading-ball-c 1.92s infinite step-start; + animation: loading-ball-r 0.48s infinite cubic-bezier(0, 0.5, 0.5, 1), loading-ball-c 1.92s infinite step-start; +} +.container .loading-container .loading-item div:nth-child(2) { + background: #e15b64; + -webkit-animation-delay: -0.48s; + -moz-animation-delay: -0.48s; + -o-animation-delay: -0.48s; + -ms-animation-delay: -0.48s; + animation-delay: -0.48s; +} +.container .loading-container .loading-item div:nth-child(3) { + background: #f47e60; + -webkit-animation-delay: -0.96s; + -moz-animation-delay: -0.96s; + -o-animation-delay: -0.96s; + -ms-animation-delay: -0.96s; + animation-delay: -0.96s; +} +.container .loading-container .loading-item div:nth-child(4) { + background: #f8b26a; + -webkit-animation-delay: -1.44s; + -moz-animation-delay: -1.44s; + -o-animation-delay: -1.44s; + -ms-animation-delay: -1.44s; + animation-delay: -1.44s; +} +.container .loading-container .loading-item div:nth-child(5) { + background: #abbd81; + -webkit-animation-delay: -1.92s; + -moz-animation-delay: -1.92s; + -o-animation-delay: -1.92s; + -ms-animation-delay: -1.92s; + animation-delay: -1.92s; +} +@-moz-keyframes loading-ball { + 0% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 25% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 50% { + -webkit-transform: translate(9.24px, 61.6px) scale(1); + -moz-transform: translate(9.24px, 61.6px) scale(1); + -o-transform: translate(9.24px, 61.6px) scale(1); + -ms-transform: translate(9.24px, 61.6px) scale(1); + transform: translate(9.24px, 61.6px) scale(1); + } + 75% { + -webkit-transform: translate(61.6px, 61.6px) scale(1); + -moz-transform: translate(61.6px, 61.6px) scale(1); + -o-transform: translate(61.6px, 61.6px) scale(1); + -ms-transform: translate(61.6px, 61.6px) scale(1); + transform: translate(61.6px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } +} +@-webkit-keyframes loading-ball { + 0% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 25% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 50% { + -webkit-transform: translate(9.24px, 61.6px) scale(1); + -moz-transform: translate(9.24px, 61.6px) scale(1); + -o-transform: translate(9.24px, 61.6px) scale(1); + -ms-transform: translate(9.24px, 61.6px) scale(1); + transform: translate(9.24px, 61.6px) scale(1); + } + 75% { + -webkit-transform: translate(61.6px, 61.6px) scale(1); + -moz-transform: translate(61.6px, 61.6px) scale(1); + -o-transform: translate(61.6px, 61.6px) scale(1); + -ms-transform: translate(61.6px, 61.6px) scale(1); + transform: translate(61.6px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } +} +@-o-keyframes loading-ball { + 0% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 25% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 50% { + -webkit-transform: translate(9.24px, 61.6px) scale(1); + -moz-transform: translate(9.24px, 61.6px) scale(1); + -o-transform: translate(9.24px, 61.6px) scale(1); + -ms-transform: translate(9.24px, 61.6px) scale(1); + transform: translate(9.24px, 61.6px) scale(1); + } + 75% { + -webkit-transform: translate(61.6px, 61.6px) scale(1); + -moz-transform: translate(61.6px, 61.6px) scale(1); + -o-transform: translate(61.6px, 61.6px) scale(1); + -ms-transform: translate(61.6px, 61.6px) scale(1); + transform: translate(61.6px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } +} +@keyframes loading-ball { + 0% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 25% { + -webkit-transform: translate(9.24px, 61.6px) scale(0); + -moz-transform: translate(9.24px, 61.6px) scale(0); + -o-transform: translate(9.24px, 61.6px) scale(0); + -ms-transform: translate(9.24px, 61.6px) scale(0); + transform: translate(9.24px, 61.6px) scale(0); + } + 50% { + -webkit-transform: translate(9.24px, 61.6px) scale(1); + -moz-transform: translate(9.24px, 61.6px) scale(1); + -o-transform: translate(9.24px, 61.6px) scale(1); + -ms-transform: translate(9.24px, 61.6px) scale(1); + transform: translate(9.24px, 61.6px) scale(1); + } + 75% { + -webkit-transform: translate(61.6px, 61.6px) scale(1); + -moz-transform: translate(61.6px, 61.6px) scale(1); + -o-transform: translate(61.6px, 61.6px) scale(1); + -ms-transform: translate(61.6px, 61.6px) scale(1); + transform: translate(61.6px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } +} +@-moz-keyframes loading-ball-r { + 0% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(0); + -moz-transform: translate(113.96px, 61.6px) scale(0); + -o-transform: translate(113.96px, 61.6px) scale(0); + -ms-transform: translate(113.96px, 61.6px) scale(0); + transform: translate(113.96px, 61.6px) scale(0); + } +} +@-webkit-keyframes loading-ball-r { + 0% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(0); + -moz-transform: translate(113.96px, 61.6px) scale(0); + -o-transform: translate(113.96px, 61.6px) scale(0); + -ms-transform: translate(113.96px, 61.6px) scale(0); + transform: translate(113.96px, 61.6px) scale(0); + } +} +@-o-keyframes loading-ball-r { + 0% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(0); + -moz-transform: translate(113.96px, 61.6px) scale(0); + -o-transform: translate(113.96px, 61.6px) scale(0); + -ms-transform: translate(113.96px, 61.6px) scale(0); + transform: translate(113.96px, 61.6px) scale(0); + } +} +@keyframes loading-ball-r { + 0% { + -webkit-transform: translate(113.96px, 61.6px) scale(1); + -moz-transform: translate(113.96px, 61.6px) scale(1); + -o-transform: translate(113.96px, 61.6px) scale(1); + -ms-transform: translate(113.96px, 61.6px) scale(1); + transform: translate(113.96px, 61.6px) scale(1); + } + 100% { + -webkit-transform: translate(113.96px, 61.6px) scale(0); + -moz-transform: translate(113.96px, 61.6px) scale(0); + -o-transform: translate(113.96px, 61.6px) scale(0); + -ms-transform: translate(113.96px, 61.6px) scale(0); + transform: translate(113.96px, 61.6px) scale(0); + } +} +@-moz-keyframes loading-ball-c { + 0% { + background: #e15b64; + } + 25% { + background: #abbd81; + } + 50% { + background: #f8b26a; + } + 75% { + background: #f47e60; + } + 100% { + background: #e15b64; + } +} +@-webkit-keyframes loading-ball-c { + 0% { + background: #e15b64; + } + 25% { + background: #abbd81; + } + 50% { + background: #f8b26a; + } + 75% { + background: #f47e60; + } + 100% { + background: #e15b64; + } +} +@-o-keyframes loading-ball-c { + 0% { + background: #e15b64; + } + 25% { + background: #abbd81; + } + 50% { + background: #f8b26a; + } + 75% { + background: #f47e60; + } + 100% { + background: #e15b64; + } +} +@keyframes loading-ball-c { + 0% { + background: #e15b64; + } + 25% { + background: #abbd81; + } + 50% { + background: #f8b26a; + } + 75% { + background: #f47e60; + } + 100% { + background: #e15b64; + } +} +blockquote.pullquote { + position: relative; + max-width: 45%; + font-size: 110%; +} +blockquote.pullquote.left { + float: left; + margin: 1em 0.5em 0 0; +} +blockquote.pullquote.right { + float: right; + margin: 1em 0 0 0.5em; +} +.video-container { + position: relative; + overflow: hidden; + margin-bottom: 16px; + padding-top: 56.25%; + height: 0; +} +.video-container iframe { + position: absolute; + top: 0; + left: 0; + margin-top: 0; + width: 100%; + height: 100%; +} +.hide-inline > .hide-button, +.hide-block > .hide-button { + display: inline-block; + padding: 5px 18px; + background: #49b1f5; + color: var(--white); + border-radius: 6px; +} +.hide-inline > .hide-button:hover, +.hide-block > .hide-button:hover { + background-color: var(--btn-hover-color); +} +.hide-inline > .hide-button.open, +.hide-block > .hide-button.open { + display: none; +} +.hide-inline > .hide-button.open + div, +.hide-block > .hide-button.open + div { + display: block; +} +.hide-inline > .hide-button.open + span, +.hide-block > .hide-button.open + span { + display: inline; +} +.hide-inline > .hide-content, +.hide-block > .hide-content { + display: none; +} +.hide-inline > .hide-button { + margin: 0 6px; +} +.hide-inline > .hide-content { + margin: 0 6px; +} +.hide-block { + margin: 0 0 16px; +} +.toggle { + margin-bottom: 20px; + border: 1px solid #f0f0f0; + border-radius: 5px; + overflow: hidden; +} +.toggle > .toggle-button { + padding: 6px 15px; + background: #f0f0f0; + color: #1f2d3d; + cursor: pointer; +} +.toggle > .toggle-content { + margin: 30px 24px; +} +.container .inline-img { + display: inline; + margin: 0 3px; + height: 1.1em; + vertical-align: text-bottom; +} +.hl-label { + padding: 2px 4px; + color: #fff; + border-radius: 3px; +} +.hl-label.default { + background-color: #777; +} +.hl-label.blue { + background-color: #428bca; +} +.hl-label.pink { + background-color: #ff69b4; +} +.hl-label.red { + background-color: #f00; +} +.hl-label.purple { + background-color: #6f42c1; +} +.hl-label.orange { + background-color: #ff8c00; +} +.hl-label.green { + background-color: #5cb85c; +} +.note { + position: relative; + margin: 0 0 20px; + padding: 15px; + border-radius: 3px; +} +.note.icon-padding { + padding-left: 3em; +} +.note > .note-icon { + position: absolute; + top: calc(50% - 0.5em); + left: 0.8em; + font-size: larger; +} +.note.blue:not(.disabled) { + border-left-color: #428bca !important; +} +.note.blue:not(.disabled).modern { + border-left-color: transparent !important; + color: #428bca; +} +.note.blue:not(.disabled):not(.simple) { + background: #e3eef7 !important; +} +.note.blue > .note-icon { + color: #428bca; +} +.note.pink:not(.disabled) { + border-left-color: #ff69b4 !important; +} +.note.pink:not(.disabled).modern { + border-left-color: transparent !important; + color: #ff69b4; +} +.note.pink:not(.disabled):not(.simple) { + background: #ffe9f4 !important; +} +.note.pink > .note-icon { + color: #ff69b4; +} +.note.red:not(.disabled) { + border-left-color: #f00 !important; +} +.note.red:not(.disabled).modern { + border-left-color: transparent !important; + color: #f00; +} +.note.red:not(.disabled):not(.simple) { + background: #ffd9d9 !important; +} +.note.red > .note-icon { + color: #f00; +} +.note.purple:not(.disabled) { + border-left-color: #6f42c1 !important; +} +.note.purple:not(.disabled).modern { + border-left-color: transparent !important; + color: #6f42c1; +} +.note.purple:not(.disabled):not(.simple) { + background: #e9e3f6 !important; +} +.note.purple > .note-icon { + color: #6f42c1; +} +.note.orange:not(.disabled) { + border-left-color: #ff8c00 !important; +} +.note.orange:not(.disabled).modern { + border-left-color: transparent !important; + color: #ff8c00; +} +.note.orange:not(.disabled):not(.simple) { + background: #ffeed9 !important; +} +.note.orange > .note-icon { + color: #ff8c00; +} +.note.green:not(.disabled) { + border-left-color: #5cb85c !important; +} +.note.green:not(.disabled).modern { + border-left-color: transparent !important; + color: #5cb85c; +} +.note.green:not(.disabled):not(.simple) { + background: #e7f4e7 !important; +} +.note.green > .note-icon { + color: #5cb85c; +} +.note.simple { + border: 1px solid #eee; + border-left-width: 5px; +} +.note.modern { + border: 1px solid transparent !important; + background-color: #f5f5f5; + color: #4c4948; +} +.note.flat { + border: initial; + border-left: 5px solid #eee; + background-color: #f9f9f9; + color: #4c4948; +} +.note h2, +.note h3, +.note h4, +.note h5, +.note h6 { + margin-top: 3px; + margin-bottom: 0; + padding-top: 0 !important; + border-bottom: initial; +} +.note p:first-child, +.note ul:first-child, +.note ol:first-child, +.note table:first-child, +.note pre:first-child, +.note blockquote:first-child, +.note img:first-child { + margin-top: 0 !important; +} +.note p:last-child, +.note ul:last-child, +.note ol:last-child, +.note table:last-child, +.note pre:last-child, +.note blockquote:last-child, +.note img:last-child { + margin-bottom: 0 !important; +} +.note .img-alt { + margin: 5px 0 10px; +} +.note:not(.no-icon) { + padding-left: 3em; +} +.note:not(.no-icon)::before { + position: absolute; + top: calc(50% - 0.95em); + left: 0.8em; + font-size: larger; +} +.note.default.flat { + background: #f7f7f7; +} +.note.default.modern { + border-color: #e1e1e1; + background: #f3f3f3; + color: #666; +} +.note.default.modern a:not(.btn) { + color: #666; +} +.note.default.modern a:not(.btn):hover { + color: #454545; +} +.note.default:not(.modern) { + border-left-color: #777; +} +.note.default:not(.modern) h2, +.note.default:not(.modern) h3, +.note.default:not(.modern) h4, +.note.default:not(.modern) h5, +.note.default:not(.modern) h6 { + color: #777; +} +.note.default:not(.no-icon)::before { + content: '\f0a9'; +} +.note.default:not(.no-icon):not(.modern)::before { + color: #777; +} +.note.primary.flat { + background: #f5f0fa; +} +.note.primary.modern { + border-color: #e1c2ff; + background: #f3daff; + color: #6f42c1; +} +.note.primary.modern a:not(.btn) { + color: #6f42c1; +} +.note.primary.modern a:not(.btn):hover { + color: #453298; +} +.note.primary:not(.modern) { + border-left-color: #6f42c1; +} +.note.primary:not(.modern) h2, +.note.primary:not(.modern) h3, +.note.primary:not(.modern) h4, +.note.primary:not(.modern) h5, +.note.primary:not(.modern) h6 { + color: #6f42c1; +} +.note.primary:not(.no-icon)::before { + content: '\f055'; +} +.note.primary:not(.no-icon):not(.modern)::before { + color: #6f42c1; +} +.note.info.flat { + background: #eef7fa; +} +.note.info.modern { + border-color: #b3e5ef; + background: #d9edf7; + color: #31708f; +} +.note.info.modern a:not(.btn) { + color: #31708f; +} +.note.info.modern a:not(.btn):hover { + color: #215761; +} +.note.info:not(.modern) { + border-left-color: #428bca; +} +.note.info:not(.modern) h2, +.note.info:not(.modern) h3, +.note.info:not(.modern) h4, +.note.info:not(.modern) h5, +.note.info:not(.modern) h6 { + color: #428bca; +} +.note.info:not(.no-icon)::before { + content: '\f05a'; +} +.note.info:not(.no-icon):not(.modern)::before { + color: #428bca; +} +.note.success.flat { + background: #eff8f0; +} +.note.success.modern { + border-color: #d0e6be; + background: #dff0d8; + color: #3c763d; +} +.note.success.modern a:not(.btn) { + color: #3c763d; +} +.note.success.modern a:not(.btn):hover { + color: #32562c; +} +.note.success:not(.modern) { + border-left-color: #5cb85c; +} +.note.success:not(.modern) h2, +.note.success:not(.modern) h3, +.note.success:not(.modern) h4, +.note.success:not(.modern) h5, +.note.success:not(.modern) h6 { + color: #5cb85c; +} +.note.success:not(.no-icon)::before { + content: '\f058'; +} +.note.success:not(.no-icon):not(.modern)::before { + color: #5cb85c; +} +.note.warning.flat { + background: #fdf8ea; +} +.note.warning.modern { + border-color: #fae4cd; + background: #fcf4e3; + color: #8a6d3b; +} +.note.warning.modern a:not(.btn) { + color: #8a6d3b; +} +.note.warning.modern a:not(.btn):hover { + color: #714f30; +} +.note.warning:not(.modern) { + border-left-color: #f0ad4e; +} +.note.warning:not(.modern) h2, +.note.warning:not(.modern) h3, +.note.warning:not(.modern) h4, +.note.warning:not(.modern) h5, +.note.warning:not(.modern) h6 { + color: #f0ad4e; +} +.note.warning:not(.no-icon)::before { + content: '\f06a'; +} +.note.warning:not(.no-icon):not(.modern)::before { + color: #f0ad4e; +} +.note.danger.flat { + background: #fcf1f2; +} +.note.danger.modern { + border-color: #ebcdd2; + background: #f2dfdf; + color: #a94442; +} +.note.danger.modern a:not(.btn) { + color: #a94442; +} +.note.danger.modern a:not(.btn):hover { + color: #84333f; +} +.note.danger:not(.modern) { + border-left-color: #d9534f; +} +.note.danger:not(.modern) h2, +.note.danger:not(.modern) h3, +.note.danger:not(.modern) h4, +.note.danger:not(.modern) h5, +.note.danger:not(.modern) h6 { + color: #d9534f; +} +.note.danger:not(.no-icon)::before { + content: '\f056'; +} +.note.danger:not(.no-icon):not(.modern)::before { + color: #d9534f; +} +.container .series-items a:hover { + color: var(--pseudo-hover); +} +.container .tabs { + position: relative; + margin: 0 0 20px; + border-right: 1px solid var(--tab-border-color); + border-bottom: 1px solid var(--tab-border-color); + border-left: 1px solid var(--tab-border-color); + border-radius: 6px; + overflow: hidden; +} +.container .tabs > .nav-tabs { + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-lines: multiple; + -moz-box-lines: multiple; + -o-box-lines: multiple; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin: 0; + padding: 0; + background: var(--tab-botton-bg); +} +.container .tabs > .nav-tabs > .tab { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + -ms-box-flex: 1; + box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + padding: 8px 18px; + border-top: 2px solid var(--tab-border-color); + background: var(--tab-botton-bg); + color: var(--tab-botton-color); + line-height: 2; + -webkit-transition: all 0.4s; + -moz-transition: all 0.4s; + -o-transition: all 0.4s; + -ms-transition: all 0.4s; + transition: all 0.4s; +} +.container .tabs > .nav-tabs > .tab i { + width: 1.5em; +} +.container .tabs > .nav-tabs > .tab.active { + border-top: 2px solid #49b1f5; + background: var(--tab-button-active-bg); + cursor: default; +} +.container .tabs > .nav-tabs > .tab:not(.active):hover { + border-top: 2px solid var(--tab-button-hover-bg); + background: var(--tab-button-hover-bg); +} +.container .tabs > .nav-tabs.no-default ~ .tab-to-top { + display: none; +} +.container .tabs > .tab-contents .tab-item-content { + position: relative; + display: none; + padding: 36px 24px 10px; +} +@media screen and (max-width: 768px) { + .container .tabs > .tab-contents .tab-item-content { + padding: 24px 14px; + } +} +.container .tabs > .tab-contents .tab-item-content.active { + display: block; + -webkit-animation: tabshow 0.5s; + -moz-animation: tabshow 0.5s; + -o-animation: tabshow 0.5s; + -ms-animation: tabshow 0.5s; + animation: tabshow 0.5s; +} +.container .tabs > .tab-contents .tab-item-content > :last-child { + margin-bottom: 0; +} +.container .tabs > .tab-to-top { + padding: 0 16px 10px 0; + width: 100%; + text-align: right; +} +.container .tabs > .tab-to-top button { + color: #99a9bf; +} +.container .tabs > .tab-to-top button:hover { + color: #49b1f5; +} +@-moz-keyframes tabshow { + 0% { + -webkit-transform: translateY(15px); + -moz-transform: translateY(15px); + -o-transform: translateY(15px); + -ms-transform: translateY(15px); + transform: translateY(15px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes tabshow { + 0% { + -webkit-transform: translateY(15px); + -moz-transform: translateY(15px); + -o-transform: translateY(15px); + -ms-transform: translateY(15px); + transform: translateY(15px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes tabshow { + 0% { + -webkit-transform: translateY(15px); + -moz-transform: translateY(15px); + -o-transform: translateY(15px); + -ms-transform: translateY(15px); + transform: translateY(15px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes tabshow { + 0% { + -webkit-transform: translateY(15px); + -moz-transform: translateY(15px); + -o-transform: translateY(15px); + -ms-transform: translateY(15px); + transform: translateY(15px); + } + 100% { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -o-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + } +} +.container .timeline { + margin: 0 10px 20px; + padding: 14px 0 5px 20px; + border-left: 2px solid var(--timeline-color, #49b1f5); +} +.container .timeline.blue { + --timeline-color: #428bca; + --timeline-bg: rgba(66,139,202, 0.2); +} +.container .timeline.pink { + --timeline-color: #ff69b4; + --timeline-bg: rgba(255,105,180, 0.2); +} +.container .timeline.red { + --timeline-color: #f00; + --timeline-bg: rgba(255,0,0, 0.2); +} +.container .timeline.purple { + --timeline-color: #6f42c1; + --timeline-bg: rgba(111,66,193, 0.2); +} +.container .timeline.orange { + --timeline-color: #ff8c00; + --timeline-bg: rgba(255,140,0, 0.2); +} +.container .timeline.green { + --timeline-color: #5cb85c; + --timeline-bg: rgba(92,184,92, 0.2); +} +.container .timeline .timeline-item { + margin: 0 0 15px; +} +.container .timeline .timeline-item:hover .item-circle:before { + border-color: var(--timeline-color, #49b1f5); +} +.container .timeline .timeline-item.headline .timeline-item-title .item-circle > p { + font-weight: 600; + font-size: 1.2em; +} +.container .timeline .timeline-item.headline .timeline-item-title .item-circle:before { + left: -28px; + border: 4px solid var(--timeline-color, #49b1f5); +} +.container .timeline .timeline-item.headline:hover .item-circle:before { + border-color: var(--pseudo-hover); +} +.container .timeline .timeline-item .timeline-item-title { + position: relative; +} +.container .timeline .timeline-item .item-circle:before { + position: absolute; + top: 50%; + left: -27px; + width: 6px; + height: 6px; + border: 3px solid var(--pseudo-hover); + border-radius: 50%; + background: var(--card-bg); + content: ''; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + -ms-transition: all 0.3s; + transition: all 0.3s; + -webkit-transform: translate(0, -50%); + -moz-transform: translate(0, -50%); + -o-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); +} +.container .timeline .timeline-item .item-circle > p { + margin: 0 0 8px; + font-weight: 500; +} +.container .timeline .timeline-item .timeline-item-content { + position: relative; + padding: 12px 15px; + border-radius: 8px; + background: var(--timeline-bg, #e4f3fd); + font-size: 0.93em; +} +.container .timeline .timeline-item .timeline-item-content > :last-child { + margin-bottom: 0; +} +.container .timeline + .timeline { + margin-top: -20px; +} +[data-theme='dark'] { + --global-bg: #0d0d0d; + --font-color: rgba(255,255,255,0.7); + --hr-border: rgba(255,255,255,0.4); + --hr-before-color: rgba(255,255,255,0.7); + --search-bg: #121212; + --search-input-color: rgba(255,255,255,0.7); + --search-a-color: rgba(255,255,255,0.7); + --preloader-bg: #0d0d0d; + --preloader-color: rgba(255,255,255,0.7); + --tab-border-color: #2c2c2c; + --tab-botton-bg: #2c2c2c; + --tab-botton-color: rgba(255,255,255,0.7); + --tab-button-hover-bg: #383838; + --tab-button-active-bg: #121212; + --card-bg: #121212; + --sidebar-bg: #121212; + --sidebar-menu-bg: #1f1f1f; + --btn-hover-color: #787878; + --btn-color: rgba(255,255,255,0.7); + --btn-bg: #1f1f1f; + --text-bg-hover: #383838; + --light-grey: rgba(255,255,255,0.7); + --dark-grey: rgba(255,255,255,0.2); + --white: rgba(255,255,255,0.9); + --text-highlight-color: rgba(255,255,255,0.9); + --blockquote-color: rgba(255,255,255,0.7); + --blockquote-bg: #2c2c2c; + --reward-pop: #2c2c2c; + --toc-link-color: rgba(255,255,255,0.6); + --scrollbar-color: #525252; + --timeline-bg: #1f1f1f; + --zoom-bg: #121212; + --mark-bg: rgba(0,0,0,0.6); +} +[data-theme='dark'] #web_bg:before { + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.7); + content: ''; +} +[data-theme='dark'] .container code { + background: #2c2c2c; +} +[data-theme='dark'] .container pre > code { + background: #171717; +} +[data-theme='dark'] .container figure.highlight { + -webkit-box-shadow: none; + box-shadow: none; +} +[data-theme='dark'] .container .note code { + background: rgba(27,31,35,0.05); +} +[data-theme='dark'] .container .aplayer { + filter: brightness(0.8); +} +[data-theme='dark'] .container kbd { + border-color: #696969; + background-color: #525252; + color: #e2f1ff; +} +[data-theme='dark'] #page-header.nav-fixed > #nav, +[data-theme='dark'] #page-header.not-top-img > #nav { + background: rgba(18,18,18,0.8); + -webkit-box-shadow: 0 5px 6px -5px rgba(133,133,133,0); + box-shadow: 0 5px 6px -5px rgba(133,133,133,0); +} +[data-theme='dark'] #post-comment .comment-switch { + background: #2c2c2c !important; +} +[data-theme='dark'] #post-comment .comment-switch #switch-btn { + filter: brightness(0.8); +} +[data-theme='dark'] .note { + filter: brightness(0.8); +} +[data-theme='dark'] .hide-button, +[data-theme='dark'] .btn-beautify, +[data-theme='dark'] .hl-label, +[data-theme='dark'] #post-outdate-notice, +[data-theme='dark'] .error-img, +[data-theme='dark'] .container iframe, +[data-theme='dark'] .gist, +[data-theme='dark'] .ads-wrap { + filter: brightness(0.8); +} +[data-theme='dark'] img { + filter: brightness(0.8); +} +[data-theme='dark'] #aside-content .aside-list > .aside-list-item:not(:last-child) { + border-bottom: 1px dashed rgba(255,255,255,0.1); +} +[data-theme='dark'] #gitalk-container { + filter: brightness(0.8); +} +[data-theme='dark'] #gitalk-container svg { + fill: rgba(255,255,255,0.9) !important; +} +[data-theme='dark'] #disqusjs #dsqjs:hover, +[data-theme='dark'] #disqusjs #dsqjs:focus, +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-tab-active, +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-no-comment { + color: rgba(255,255,255,0.7); +} +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-order-label { + background-color: #1f1f1f; +} +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-post-body { + color: rgba(255,255,255,0.7); +} +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-post-body code, +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-post-body pre { + background: #2c2c2c; +} +[data-theme='dark'] #disqusjs #dsqjs .dsqjs-post-body blockquote { + color: rgba(255,255,255,0.7); +} +[data-theme='dark'] #artitalk_main #lazy { + background: #121212; +} +[data-theme='dark'] #operare_artitalk .c2 { + background: #121212; +} +@media screen and (max-width: 900px) { + [data-theme='dark'] #card-toc { + background: #1f1f1f; + } +} +[data-theme='dark'] .artalk.atk-dark-mode, +[data-theme='dark'] .atk-layer-wrap.atk-dark-mode { + --at-color-font: rgba(255,255,255,0.7); + --at-color-meta: rgba(255,255,255,0.7); + --at-color-grey: rgba(255,255,255,0.7); +} +[data-theme='dark'] .atk-send-btn, +[data-theme='dark'] .atk-badge { + color: rgba(255,255,255,0.7) !important; +} +[data-theme='dark'] #waline-wrap { + --waline-color: rgba(255,255,255,0.7); + --waline-dark-grey: rgba(255,255,255,0.7); + --waline-info-color: rgba(255,255,255,0.5); +} +.read-mode { + --font-color: #4c4948; + --readmode-light-color: #fff; + --white: #4c4948; + --light-grey: #4c4948; + --gray: #d6dbdf; + --hr-border: #d6dbdf; + --hr-before-color: #b9c2c9; + --highlight-bg: #f7f7f7; + --exit-btn-bg: #c0c0c0; + --exit-btn-color: #fff; + --exit-btn-hover: #8d8d8d; + --pseudo-hover: none; +} +[data-theme='dark'] .read-mode { + --font-color: rgba(255,255,255,0.7); + --readmode-light-color: #0d0d0d; + --white: rgba(255,255,255,0.9); + --light-grey: rgba(255,255,255,0.7); + --gray: rgba(255,255,255,0.7); + --hr-border: rgba(255,255,255,0.5); + --hr-before-color: rgba(255,255,255,0.7); + --highlight-bg: #171717; + --exit-btn-bg: #1f1f1f; + --exit-btn-color: rgba(255,255,255,0.9); + --exit-btn-hover: #525252; +} +.read-mode { + background: var(--readmode-light-color); +} +.read-mode .exit-readmode { + position: fixed; + top: 30px; + right: 30px; + z-index: 100; + width: 40px; + height: 40px; + background: var(--exit-btn-bg); + color: var(--exit-btn-color); + font-size: 16px; + -webkit-transition: background 0.3s; + -moz-transition: background 0.3s; + -o-transition: background 0.3s; + -ms-transition: background 0.3s; + transition: background 0.3s; + border-radius: 8px; +} +@media screen and (max-width: 768px) { + .read-mode .exit-readmode { + top: initial; + bottom: 30px; + } +} +.read-mode .exit-readmode:hover { + background: var(--exit-btn-hover); +} +.read-mode #aside-content { + display: none; +} +.read-mode #page-header.post-bg { + background: none !important; +} +.read-mode #page-header.post-bg:before { + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); +} +.read-mode #page-header.post-bg > #post-info { + text-align: center; +} +.read-mode #post { + margin: 0 auto; + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.read-mode #post:hover { + -webkit-box-shadow: none; + box-shadow: none; +} +.read-mode > canvas { + display: none !important; +} +.read-mode .highlight-tools, +.read-mode #footer, +.read-mode #post > *:not(#post-info):not(.post-content), +.read-mode #nav, +.read-mode #post-outdate-notice, +.read-mode #web_bg, +.read-mode #rightside, +.read-mode .not-top-img { + display: none !important; +} +.read-mode .container a { + color: #99a9bf; +} +.read-mode .container pre, +.read-mode .container .highlight:not(.js-file-line-container) { + background: var(--highlight-bg) !important; +} +.read-mode .container pre *, +.read-mode .container .highlight:not(.js-file-line-container) * { + color: var(--font-color) !important; +} +.read-mode .container figure.highlight { + border-radius: 0 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; +} +.read-mode .container figure.highlight > :not(.highlight-tools) { + display: block !important; +} +.read-mode .container figure.highlight .line:before { + color: var(--font-color) !important; +} +.read-mode .container figure.highlight .hljs { + background: var(--highlight-bg) !important; +} +.read-mode .container h1, +.read-mode .container h2, +.read-mode .container h3, +.read-mode .container h4, +.read-mode .container h5, +.read-mode .container h6 { + padding: 0; +} +.read-mode .container h1:before, +.read-mode .container h2:before, +.read-mode .container h3:before, +.read-mode .container h4:before, +.read-mode .container h5:before, +.read-mode .container h6:before { + content: ''; +} +.read-mode .container h1:hover, +.read-mode .container h2:hover, +.read-mode .container h3:hover, +.read-mode .container h4:hover, +.read-mode .container h5:hover, +.read-mode .container h6:hover { + padding: 0; +} +.read-mode .container ul:hover:before, +.read-mode .container li:hover:before, +.read-mode .container ol:hover:before { + -webkit-transform: none !important; + -moz-transform: none !important; + -o-transform: none !important; + -ms-transform: none !important; + transform: none !important; +} +.read-mode .container ol:before, +.read-mode .container li:before { + background: transparent !important; + color: var(--font-color) !important; +} +.read-mode .container ul >li:before { + border-color: var(--gray) !important; +} +.read-mode .container .tabs { + border: 2px solid var(--tab-border-color); +} +.read-mode .container .tabs > .nav-tabs { + background: transparent; +} +.read-mode .container .tabs > .nav-tabs > .tab { + border-top: none !important; +} +.read-mode .container .tabs > .tab-contents .tab-item-content.active { + -webkit-animation: none; + -moz-animation: none; + -o-animation: none; + -ms-animation: none; + animation: none; +} +.read-mode .container code { + color: var(--font-color); +} +.read-mode .container blockquote { + border-color: var(--gray); + background-color: var(--readmode-light-color); +} +.read-mode .container kbd { + border: 1px solid var(--gray); + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; + color: var(--font-color); +} +.read-mode .container .hide-toggle { + border: 1px solid var(--gray) !important; +} +.read-mode .container .hide-button, +.read-mode .container .btn-beautify, +.read-mode .container .hl-label { + border: 1px solid var(--gray) !important; + background: var(--readmode-light-color) !important; + color: var(--font-color) !important; +} +.read-mode .container .note { + border: 2px solid var(--gray); + border-left-color: var(--gray) !important; + filter: none; + background-color: var(--readmode-light-color) !important; + color: var(--font-color); +} +.read-mode .container .note:before, +.read-mode .container .note .note-icon { + color: var(--font-color); +} +.search-dialog { + position: fixed; + top: 10%; + left: 50%; + z-index: 1001; + display: none; + margin-left: -300px; + padding: 20px; + width: 600px; + background: var(--search-bg); + --search-height: 100vh; + border-radius: 8px; +} +@media screen and (max-width: 768px) { + .search-dialog { + top: 0; + left: 0; + margin: 0; + width: 100%; + height: 100%; + border-radius: 0; + } +} +.search-dialog .search-nav { + margin: 0 0 14px; + color: #49b1f5; + font-size: 1.4em; + line-height: 1; +} +.search-dialog .search-nav .search-dialog-title { + margin-right: 10px; +} +.search-dialog .search-nav .search-close-button { + float: right; + color: #858585; + -webkit-transition: color 0.2s ease-in-out; + -moz-transition: color 0.2s ease-in-out; + -o-transition: color 0.2s ease-in-out; + -ms-transition: color 0.2s ease-in-out; + transition: color 0.2s ease-in-out; +} +.search-dialog .search-nav .search-close-button:hover { + color: #49b1f5; +} +.search-dialog hr { + margin: 15px auto; +} +#search-mask { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1000; + display: none; + background: rgba(0,0,0,0.6); +} diff --git a/css/var.css b/css/var.css new file mode 100644 index 000000000..e69de29bb diff --git a/themes/hexo-theme-butterfly-3.3.0/source/img/404.jpg b/img/404.jpg old mode 100755 new mode 100644 similarity index 100% rename from themes/hexo-theme-butterfly-3.3.0/source/img/404.jpg rename to img/404.jpg diff --git a/img/butterfly-icon.png b/img/butterfly-icon.png new file mode 100644 index 000000000..3992d7740 Binary files /dev/null and b/img/butterfly-icon.png differ diff --git a/img/error-page.png b/img/error-page.png new file mode 100644 index 000000000..9d1de9657 Binary files /dev/null and b/img/error-page.png differ diff --git a/img/favicon.ico b/img/favicon.ico new file mode 100644 index 000000000..56d07f553 Binary files /dev/null and b/img/favicon.ico differ diff --git a/themes/hexo-theme-butterfly-3.3.0/source/img/friend_404.gif b/img/friend_404.gif old mode 100755 new mode 100644 similarity index 100% rename from themes/hexo-theme-butterfly-3.3.0/source/img/friend_404.gif rename to img/friend_404.gif diff --git a/index.html b/index.html new file mode 100644 index 000000000..ea7bc83b7 --- /dev/null +++ b/index.html @@ -0,0 +1,237 @@ +淳淳同学的个人博客 - 人不可一日无事 + + + + + + + + + +
    【个人开源】Vue3 轮播组件: v3-carousel
    Hello World
    Vue源码解读(知识点总结)
    2021前端知识点总结
    JavaScript运算符优先级
    预防XSS攻击插件 js-xss
    VSCode注释高亮插件 Better Comments
    【转】看完让你彻底搞懂Websocket原理
    加载聊天历史记录并保留滚动条当前位置
    查看代码行数
    \ No newline at end of file diff --git a/source/js/calendar.js b/js/calendar.js similarity index 100% rename from source/js/calendar.js rename to js/calendar.js diff --git a/source/js/gitcalendar.js b/js/gitcalendar.js similarity index 55% rename from source/js/gitcalendar.js rename to js/gitcalendar.js index 3775d0432..ceaf6b31a 100644 --- a/source/js/gitcalendar.js +++ b/js/gitcalendar.js @@ -89,141 +89,14 @@ var _typeof = "function" == typeof Symbol && "symbol" == _typeof2(Symbol.iterato }, {}], 4: [function (e, t, r) { var n = e("iterate-object"), a = e("sliced");function o(r, e) { - return "string" == typeof r ? "<" === r.charAt(0) ? (r = document.createElement(r.slice(1, -1)), n(e || {}, function (e, t) { - switch (t) {case "text": - return void (r.textContent = e);case "html": - return void (r.innerHTML = e);}r.setAttribute(t, e); - }), r) : (e = e || document).querySelector(r) : r; - }o.$$ = function (e, t) { - return "string" == typeof e ? (t = t || document, a(t.querySelectorAll(e))) : [e]; - }, t.exports = o; - }, { "iterate-object": 9, sliced: 13 }], 5: [function (e, t, r) { - t.exports = function (e, t, r) { - r = r || "0";var n = (t = t || 2) - (e = e.toString()).length;return (n <= 0 ? "" : r.repeat(n)) + e; - }; - }, {}], 6: [function (e, t, r) { - var n = e("months"), - a = e("days"), - o = e("fillo"), - s = e("parse-it").Parser, - u = { YYYY: function YYYY(e, t) { - return t ? e.getUTCFullYear() : e.getFullYear(); - }, YY: function YY(e, t) { - return u.YYYY(e, t) % 100; - }, MMMM: function MMMM(e, t) { - return t ? n[e.getUTCMonth()] : n[e.getMonth()]; - }, MMM: function MMM(e, t) { - return t ? n.abbr[e.getUTCMonth()] : n.abbr[e.getMonth()]; - }, MM: function MM(e, t) { - return o(t ? e.getUTCMonth() + 1 : e.getMonth() + 1); - }, M: function M(e, t) { - return t ? e.getUTCMonth() + 1 : e.getMonth() + 1; - }, dddd: function dddd(e, t) { - return a[u.d(e, t)]; - }, ddd: function ddd(e, t) { - return a.abbr[u.d(e, t)]; - }, dd: function dd(e, t) { - return a.short[u.d(e, t)]; - }, d: function d(e, t) { - return t ? e.getUTCDay() : e.getDay(); - }, DD: function DD(e, t) { - return o(u.D(e, t)); - }, D: function D(e, t) { - return t ? e.getUTCDate() : e.getDate(); - }, A: function A(e, t) { - return u.a(e, t).toUpperCase(); - }, a: function a(e, t) { - return 12 <= u.H(e, t) ? "pm" : "am"; - }, hh: function hh(e, t) { - return o(u.h(e, t)); - }, h: function h(e, t) { - return u.H(e, t) % 12 || 12; - }, HH: function HH(e, t) { - return o(u.H(e, t)); - }, H: function H(e, t) { - return t ? e.getUTCHours() : e.getHours(); - }, mm: function mm(e, t) { - return o(u.m(e, t)); - }, m: function m(e, t) { - return t ? e.getUTCMinutes() : e.getMinutes(); - }, ss: function ss(e, t) { - return o(u.s(e, t)); - }, s: function s(e, t) { - return t ? e.getUTCSeconds() : e.getSeconds(); - }, S: function S(e, t) { - return Math.round(u.s(e, t) / 60 * 10); - }, SS: function SS(e, t) { - return o(u.s(e, t) / 60 * 100); - }, SSS: function SSS(e, t) { - return o(u.s(e, t) / 60 * 1e3, 3); - }, Z: function Z(e) { - var t = -e.getTimezoneOffset();return (0 <= t ? "+" : "-") + o(parseInt(t / 60)) + ":" + o(t % 60); - }, ZZ: function ZZ(e) { - var t = -e.getTimezoneOffset();return (0 <= t ? "+" : "-") + o(parseInt(t / 60)) + o(t % 60); - } }, - i = new s(u);t.exports = function (e, t) { - return i.run(t, [e, e._useUTC]); - }; - }, { days: 3, fillo: 5, months: 10, "parse-it": 11 }], 7: [function (e, t, r) { - t.exports = ["#eee", "#d6e685", "#8cc665", "#44a340", "#1e6823"]; - }, {}], 8: [function (e, t, r) { - var i = e("github-calendar-legend");t.exports = function (e) { - function o() { - s.current_streak > s.longest_streak && (s.longest_streak = s.current_streak, s.longest_streak_range[0] = s.current_streak_range[0], s.longest_streak_range[1] = s.current_streak_range[1]); + return "string" == typeof r ? "<" 1 11 12 13 60 =="=" r.charat(0) ? (r="document.createElement(r.slice(1," -1)), n(e || {}, function (e, t) { switch (t) {case "text": return void (r.textcontent="e);case" "html": (r.innerhtml="e);}r.setAttribute(t," e); }), r) : (e="e" document).queryselector(r) r; }o.$$="function" "string"="=" typeof e (t="t" document, a(t.queryselectorall(e))) [e]; }, t.exports="o;" "iterate-object": 9, sliced: }], 5: [function t, r="r" "0";var n="(t" t 2) - (n <="0" "" r.repeat(n)) + e; }; {}], 6: var a="e("days")," o="e("fillo")," s="e("parse-it").Parser," u="{" yyyy: yyyy(e, e.getutcfullyear() e.getfullyear(); yy: yy(e, u.yyyy(e, % 100; mmmm: mmmm(e, n[e.getutcmonth()] n[e.getmonth()]; mmm: mmm(e, n.abbr[e.getutcmonth()] n.abbr[e.getmonth()]; mm: mm(e, o(t e.getutcmonth() e.getmonth() 1); m: m(e, 1; dddd: dddd(e, a[u.d(e, t)]; ddd: ddd(e, a.abbr[u.d(e, dd: dd(e, a.short[u.d(e, d: d(e, e.getutcday() e.getday(); o(u.d(e, t)); e.getutcdate() e.getdate(); a: a(e, u.a(e, t).touppercase(); "pm" "am"; hh: hh(e, o(u.h(e, h: h(e, u.h(e, 12; e.getutchours() e.gethours(); o(u.m(e, e.getutcminutes() e.getminutes(); ss: ss(e, o(u.s(e, s: s(e, e.getutcseconds() e.getseconds(); math.round(u.s(e, * 10); 100); sss: sss(e, 1e3, 3); z: z(e) (0 "+" "-") o(parseint(t 60)) ":" 60); zz: zz(e) } i="new" s(u);t.exports="function" i.run(t, [e, e._useutc]); days: 3, fillo: 5, months: 10, "parse-it": 7: "#d6e685", "#8cc665", "#44a340", "#1e6823"]; 8: (e) o() s.current_streak> s.longest_streak && (s.longest_streak = s.current_streak, s.longest_streak_range[0] = s.current_streak_range[0], s.longest_streak_range[1] = s.current_streak_range[1]); }var s = { last_year: 0, longest_streak: -1, longest_streak_range: [], current_streak: 0, current_streak_range: [], weeks: [], days: [], last_contributed: null }, u = [];return e.split("\n").slice(2).map(function (e) { return e.trim(); }).forEach(function (e) { - if (e.startsWith(" o;) { + if (e.startsWith(" o;) { n[a - o] = e[a]; }return n; }; }, {}] }, {}, [1])(1); -}); \ No newline at end of file +}); \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 000000000..20f509a87 --- /dev/null +++ b/js/main.js @@ -0,0 +1,930 @@ +document.addEventListener('DOMContentLoaded', () => { + let headerContentWidth, $nav + let mobileSidebarOpen = false + + const adjustMenu = init => { + const getAllWidth = ele => Array.from(ele).reduce((width, i) => width + i.offsetWidth, 0) + + if (init) { + const blogInfoWidth = getAllWidth(document.querySelector('#blog-info > a').children) + const menusWidth = getAllWidth(document.getElementById('menus').children) + headerContentWidth = blogInfoWidth + menusWidth + $nav = document.getElementById('nav') + } + + const hideMenuIndex = window.innerWidth <= 768 || headerContentWidth > $nav.offsetWidth - 120 + $nav.classList.toggle('hide-menu', hideMenuIndex) + } + + // 初始化header + const initAdjust = () => { + adjustMenu(true) + $nav.classList.add('show') + } + + // sidebar menus + const sidebarFn = { + open: () => { + btf.overflowPaddingR.add() + btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s') + document.getElementById('sidebar-menus').classList.add('open') + mobileSidebarOpen = true + }, + close: () => { + btf.overflowPaddingR.remove() + btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s') + document.getElementById('sidebar-menus').classList.remove('open') + mobileSidebarOpen = false + } + } + + /** + * 首頁top_img底下的箭頭 + */ + const scrollDownInIndex = () => { + const handleScrollToDest = () => { + btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300) + } + + const $scrollDownEle = document.getElementById('scroll-down') + $scrollDownEle && btf.addEventListenerPjax($scrollDownEle, 'click', handleScrollToDest) + } + + /** + * 代碼 + * 只適用於Hexo默認的代碼渲染 + */ + const addHighlightTool = () => { + const highLight = GLOBAL_CONFIG.highlight + if (!highLight) return + + const { highlightCopy, highlightLang, highlightHeightLimit, highlightFullpage, highlightMacStyle, plugin } = highLight + const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink + const isShowTool = highlightCopy || highlightLang || isHighlightShrink !== undefined || highlightFullpage || highlightMacStyle + const $figureHighlight = plugin === 'highlight.js' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]') + + if (!((isShowTool || highlightHeightLimit) && $figureHighlight.length)) return + + const isPrismjs = plugin === 'prismjs' + const highlightShrinkClass = isHighlightShrink === true ? 'closed' : '' + const highlightShrinkEle = isHighlightShrink !== undefined ? '' : '' + const highlightCopyEle = highlightCopy ? '
    ' : '' + const highlightMacStyleEle = '
    ' + const highlightFullpageEle = highlightFullpage ? '' : '' + + const alertInfo = (ele, text) => { + if (GLOBAL_CONFIG.Snackbar !== undefined) { + btf.snackbarShow(text) + } else { + ele.textContent = text + ele.style.opacity = 1 + setTimeout(() => { ele.style.opacity = 0 }, 800) + } + } + + const copy = async (text, ctx) => { + try { + await navigator.clipboard.writeText(text) + alertInfo(ctx, GLOBAL_CONFIG.copy.success) + } catch (err) { + console.error('Failed to copy: ', err) + alertInfo(ctx, GLOBAL_CONFIG.copy.noSupport) + } + } + + // click events + const highlightCopyFn = (ele, clickEle) => { + const $buttonParent = ele.parentNode + $buttonParent.classList.add('copy-true') + const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre' + const codeElement = $buttonParent.querySelector(preCodeSelector) + if (!codeElement) return + copy(codeElement.innerText, clickEle.previousElementSibling) + $buttonParent.classList.remove('copy-true') + } + + const highlightShrinkFn = ele => ele.classList.toggle('closed') + + const codeFullpage = (item, clickEle) => { + const wrapEle = item.closest('figure.highlight') + const isFullpage = wrapEle.classList.toggle('code-fullpage') + + document.body.style.overflow = isFullpage ? 'hidden' : '' + clickEle.classList.toggle('fa-down-left-and-up-right-to-center', isFullpage) + clickEle.classList.toggle('fa-up-right-and-down-left-from-center', !isFullpage) + } + + const highlightToolsFn = e => { + const $target = e.target.classList + const currentElement = e.currentTarget + if ($target.contains('expand')) highlightShrinkFn(currentElement) + else if ($target.contains('copy-button')) highlightCopyFn(currentElement, e.target) + else if ($target.contains('fullpage-button')) codeFullpage(currentElement, e.target) + } + + const expandCode = e => e.currentTarget.classList.toggle('expand-done') + + // 獲取隱藏狀態下元素的真實高度 + const getActualHeight = item => { + const hiddenElements = new Map() + + const fix = () => { + let current = item + while (current !== document.body && current != null) { + if (window.getComputedStyle(current).display === 'none') { + hiddenElements.set(current, current.getAttribute('style') || '') + } + current = current.parentNode + } + + const style = 'visibility: hidden !important; display: block !important;' + hiddenElements.forEach((originalStyle, elem) => { + elem.setAttribute('style', originalStyle ? originalStyle + ';' + style : style) + }) + } + + const restore = () => { + hiddenElements.forEach((originalStyle, elem) => { + if (originalStyle === '') elem.removeAttribute('style') + else elem.setAttribute('style', originalStyle) + }) + } + + fix() + const height = item.offsetHeight + restore() + return height + } + + const createEle = (lang, item) => { + const fragment = document.createDocumentFragment() + + if (isShowTool) { + const hlTools = document.createElement('div') + hlTools.className = `highlight-tools ${highlightShrinkClass}` + hlTools.innerHTML = highlightMacStyleEle + highlightShrinkEle + lang + highlightCopyEle + highlightFullpageEle + btf.addEventListenerPjax(hlTools, 'click', highlightToolsFn) + fragment.appendChild(hlTools) + } + + if (highlightHeightLimit && getActualHeight(item) > highlightHeightLimit + 30) { + const ele = document.createElement('div') + ele.className = 'code-expand-btn' + ele.innerHTML = '' + btf.addEventListenerPjax(ele, 'click', expandCode) + fragment.appendChild(ele) + } + + isPrismjs ? item.parentNode.insertBefore(fragment, item) : item.insertBefore(fragment, item.firstChild) + } + + $figureHighlight.forEach(item => { + let langName = '' + if (isPrismjs) btf.wrap(item, 'figure', { class: 'highlight' }) + + if (!highlightLang) { + createEle('', item) + return + } + + if (isPrismjs) { + langName = item.getAttribute('data-language') || 'Code' + } else { + langName = item.getAttribute('class').split(' ')[1] + if (langName === 'plain' || langName === undefined) langName = 'Code' + } + createEle(`
    ${langName}
    `, item) + }) + } + + /** + * PhotoFigcaption + */ + const addPhotoFigcaption = () => { + if (!GLOBAL_CONFIG.isPhotoFigcaption) return + document.querySelectorAll('#article-container img').forEach(item => { + const altValue = item.title || item.alt + if (!altValue) return + const ele = document.createElement('div') + ele.className = 'img-alt text-center' + ele.textContent = altValue + item.insertAdjacentElement('afterend', ele) + }) + } + + /** + * Lightbox + */ + const runLightbox = () => { + btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)')) + } + + /** + * justified-gallery 圖庫排版 + */ + + const fetchUrl = async url => { + try { + const response = await fetch(url) + return await response.json() + } catch (error) { + console.error('Failed to fetch URL:', error) + return [] + } + } + + const runJustifiedGallery = (container, data, config) => { + const { isButton, limit, firstLimit, tabs } = config + + const dataLength = data.length + const maxGroupKey = Math.ceil((dataLength - firstLimit) / limit + 1) + + // Gallery configuration + const igConfig = { + gap: 5, + isConstantSize: true, + sizeRange: [150, 600], + // useResizeObserver: true, + // observeChildren: true, + useTransform: true + // useRecycle: false + } + + const ig = new InfiniteGrid.JustifiedInfiniteGrid(container, igConfig) + let isLayoutHidden = false + + // Utility functions + const sanitizeString = str => (str && str.replace(/"/g, '"')) || '' + + const createImageItem = item => { + const alt = item.alt ? `alt="${sanitizeString(item.alt)}"` : '' + const title = item.title ? `title="${sanitizeString(item.title)}"` : '' + return `
    + +
    ` + } + + const getItems = (nextGroupKey, count, isFirst = false) => { + const startIndex = isFirst ? (nextGroupKey - 1) * count : (nextGroupKey - 2) * count + firstLimit + return data.slice(startIndex, startIndex + count).map(createImageItem) + } + + // Load more button + const addLoadMoreButton = container => { + const button = document.createElement('button') + button.innerHTML = `${GLOBAL_CONFIG.infinitegrid.buttonText}` + + button.addEventListener('click', () => { + button.remove() + btf.setLoading.add(container) + appendItems(ig.getGroups().length + 1, limit) + }, { once: true }) + + container.insertAdjacentElement('afterend', button) + } + + const appendItems = (nextGroupKey, count, isFirst) => { + ig.append(getItems(nextGroupKey, count, isFirst), nextGroupKey) + } + + // Event handlers + const handleRenderComplete = e => { + if (tabs) { + const parentNode = container.parentNode + if (isLayoutHidden) { + parentNode.style.visibility = 'visible' + } + if (container.offsetHeight === 0) { + parentNode.style.visibility = 'hidden' + isLayoutHidden = true + } + } + + const { updated, isResize, mounted } = e + if (!updated.length || !mounted.length || isResize) return + + btf.loadLightbox(container.querySelectorAll('img:not(.medium-zoom-image)')) + + if (ig.getGroups().length === maxGroupKey) { + btf.setLoading.remove(container) + !tabs && ig.off('renderComplete', handleRenderComplete) + return + } + + if (isButton) { + btf.setLoading.remove(container) + addLoadMoreButton(container) + } + } + + const handleRequestAppend = btf.debounce(e => { + const nextGroupKey = (+e.groupKey || 0) + 1 + + if (nextGroupKey === 1) appendItems(nextGroupKey, firstLimit, true) + else appendItems(nextGroupKey, limit) + + if (nextGroupKey === maxGroupKey) ig.off('requestAppend', handleRequestAppend) + }, 300) + + btf.setLoading.add(container) + ig.on('renderComplete', handleRenderComplete) + + if (isButton) { + appendItems(1, firstLimit, true) + } else { + ig.on('requestAppend', handleRequestAppend) + ig.renderItems() + } + + btf.addGlobalFn('pjaxSendOnce', () => ig.destroy()) + } + + const addJustifiedGallery = async (elements, tabs = false) => { + if (!elements.length) return + + const initGallery = async () => { + for (const element of elements) { + if (btf.isHidden(element) || element.classList.contains('loaded')) continue + + const config = { + isButton: element.getAttribute('data-button') === 'true', + limit: parseInt(element.getAttribute('data-limit'), 10), + firstLimit: parseInt(element.getAttribute('data-first'), 10), + tabs + } + + const container = element.firstElementChild + const content = container.textContent + container.textContent = '' + element.classList.add('loaded') + + try { + const data = element.getAttribute('data-type') === 'url' ? await fetchUrl(content) : JSON.parse(content) + runJustifiedGallery(container, data, config) + } catch (error) { + console.error('Gallery data parsing failed:', error) + } + } + } + + if (typeof InfiniteGrid === 'function') { + await initGallery() + } else { + await btf.getScript(GLOBAL_CONFIG.infinitegrid.js) + await initGallery() + } + } + + /** + * rightside scroll percent + */ + const rightsideScrollPercent = currentTop => { + const scrollPercent = btf.getScrollPercent(currentTop, document.body) + const goUpElement = document.getElementById('go-up') + + if (scrollPercent < 95) { + goUpElement.classList.add('show-percent') + goUpElement.querySelector('.scroll-percent').textContent = scrollPercent + } else { + goUpElement.classList.remove('show-percent') + } + } + + /** + * 滾動處理 + */ + const scrollFn = () => { + const $rightside = document.getElementById('rightside') + const innerHeight = window.innerHeight + 56 + let initTop = 0 + const $header = document.getElementById('page-header') + const isChatBtn = typeof chatBtn !== 'undefined' + const isShowPercent = GLOBAL_CONFIG.percent.rightside + + // 檢查文檔高度是否小於視窗高度 + const checkDocumentHeight = () => { + if (document.body.scrollHeight <= innerHeight) { + $rightside.classList.add('rightside-show') + return true + } + return false + } + + // 如果文檔高度小於視窗高度,直接返回 + if (checkDocumentHeight()) return + + // find the scroll direction + const scrollDirection = currentTop => { + const result = currentTop > initTop // true is down & false is up + initTop = currentTop + return result + } + + let flag = '' + const scrollTask = btf.throttle(() => { + const currentTop = window.scrollY || document.documentElement.scrollTop + const isDown = scrollDirection(currentTop) + if (currentTop > 56) { + if (flag === '') { + $header.classList.add('nav-fixed') + $rightside.classList.add('rightside-show') + } + + if (isDown) { + if (flag !== 'down') { + $header.classList.remove('nav-visible') + isChatBtn && window.chatBtn.hide() + flag = 'down' + } + } else { + if (flag !== 'up') { + $header.classList.add('nav-visible') + isChatBtn && window.chatBtn.show() + flag = 'up' + } + } + } else { + flag = '' + if (currentTop === 0) { + $header.classList.remove('nav-fixed', 'nav-visible') + } + $rightside.classList.remove('rightside-show') + } + + isShowPercent && rightsideScrollPercent(currentTop) + checkDocumentHeight() + }, 300) + + btf.addEventListenerPjax(window, 'scroll', scrollTask, { passive: true }) + } + + /** + * toc,anchor + */ + const scrollFnToDo = () => { + const isToc = GLOBAL_CONFIG_SITE.isToc + const isAnchor = GLOBAL_CONFIG.isAnchor + const $article = document.getElementById('article-container') + + if (!($article && (isToc || isAnchor))) return + + let $tocLink, $cardToc, autoScrollToc, $tocPercentage, isExpand + + if (isToc) { + const $cardTocLayout = document.getElementById('card-toc') + $cardToc = $cardTocLayout.querySelector('.toc-content') + $tocLink = $cardToc.querySelectorAll('.toc-link') + $tocPercentage = $cardTocLayout.querySelector('.toc-percentage') + isExpand = $cardToc.classList.contains('is-expand') + + // toc元素點擊 + const tocItemClickFn = e => { + const target = e.target.closest('.toc-link') + if (!target) return + + e.preventDefault() + btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI(target.getAttribute('href')).replace('#', ''))), 300) + if (window.innerWidth < 900) { + $cardTocLayout.classList.remove('open') + } + } + + btf.addEventListenerPjax($cardToc, 'click', tocItemClickFn) + + autoScrollToc = item => { + const sidebarHeight = $cardToc.clientHeight + const itemOffsetTop = item.offsetTop + const itemHeight = item.clientHeight + const scrollTop = $cardToc.scrollTop + const offset = itemOffsetTop - scrollTop + const middlePosition = (sidebarHeight - itemHeight) / 2 + + if (offset !== middlePosition) { + $cardToc.scrollTop = scrollTop + (offset - middlePosition) + } + } + + // 處理 hexo-blog-encrypt 事件 + $cardToc.style.display = 'block' + } + + // find head position & add active class + const $articleList = $article.querySelectorAll('h1,h2,h3,h4,h5,h6') + let detectItem = '' + + const findHeadPosition = top => { + if (top === 0) return false + + let currentId = '' + let currentIndex = '' + + for (let i = 0; i < $articleList.length; i++) { + const ele = $articleList[i] + if (top > btf.getEleTop(ele) - 80) { + const id = ele.id + currentId = id ? '#' + encodeURI(id) : '' + currentIndex = i + } else { + break + } + } + + if (detectItem === currentIndex) return + + if (isAnchor) btf.updateAnchor(currentId) + + detectItem = currentIndex + + if (isToc) { + $cardToc.querySelectorAll('.active').forEach(i => i.classList.remove('active')) + + if (currentId) { + const currentActive = $tocLink[currentIndex] + currentActive.classList.add('active') + + setTimeout(() => autoScrollToc(currentActive), 0) + + if (!isExpand) { + let parent = currentActive.parentNode + while (!parent.matches('.toc')) { + if (parent.matches('li')) parent.classList.add('active') + parent = parent.parentNode + } + } + } + } + } + + // main of scroll + const tocScrollFn = btf.throttle(() => { + const currentTop = window.scrollY || document.documentElement.scrollTop + if (isToc && GLOBAL_CONFIG.percent.toc) { + $tocPercentage.textContent = btf.getScrollPercent(currentTop, $article) + } + findHeadPosition(currentTop) + }, 100) + + btf.addEventListenerPjax(window, 'scroll', tocScrollFn, { passive: true }) + } + + const handleThemeChange = mode => { + const globalFn = window.globalFn || {} + const themeChange = globalFn.themeChange || {} + if (!themeChange) { + return + } + + Object.keys(themeChange).forEach(key => { + const themeChangeFn = themeChange[key] + if (['disqus', 'disqusjs'].includes(key)) { + setTimeout(() => themeChangeFn(mode), 300) + } else { + themeChangeFn(mode) + } + }) + } + + /** + * Rightside + */ + const rightSideFn = { + readmode: () => { // read mode + const $body = document.body + const newEle = document.createElement('button') + + const exitReadMode = () => { + $body.classList.remove('read-mode') + newEle.remove() + newEle.removeEventListener('click', exitReadMode) + } + + $body.classList.add('read-mode') + newEle.type = 'button' + newEle.className = 'fas fa-sign-out-alt exit-readmode' + newEle.addEventListener('click', exitReadMode) + $body.appendChild(newEle) + }, + darkmode: () => { // switch between light and dark mode + const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark' + if (willChangeMode === 'dark') { + btf.activateDarkMode() + GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night) + } else { + btf.activateLightMode() + GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day) + } + btf.saveToLocal.set('theme', willChangeMode, 2) + handleThemeChange(willChangeMode) + }, + 'rightside-config': item => { // Show or hide rightside-hide-btn + const hideLayout = item.firstElementChild + if (hideLayout.classList.contains('show')) { + hideLayout.classList.add('status') + setTimeout(() => { + hideLayout.classList.remove('status') + }, 300) + } + + hideLayout.classList.toggle('show') + }, + 'go-up': () => { // Back to top + btf.scrollToDest(0, 500) + }, + 'hide-aside-btn': () => { // Hide aside + const $htmlDom = document.documentElement.classList + const saveStatus = $htmlDom.contains('hide-aside') ? 'show' : 'hide' + btf.saveToLocal.set('aside-status', saveStatus, 2) + $htmlDom.toggle('hide-aside') + }, + 'mobile-toc-button': (p, item) => { // Show mobile toc + const tocEle = document.getElementById('card-toc') + tocEle.style.transition = 'transform 0.3s ease-in-out' + + const tocEleHeight = tocEle.clientHeight + const btData = item.getBoundingClientRect() + + const tocEleBottom = window.innerHeight - btData.bottom - 30 + if (tocEleHeight > tocEleBottom) { + tocEle.style.transformOrigin = `right ${tocEleHeight - tocEleBottom - btData.height / 2}px` + } + + tocEle.classList.toggle('open') + tocEle.addEventListener('transitionend', () => { + tocEle.style.cssText = '' + }, { once: true }) + }, + 'chat-btn': () => { // Show chat + window.chatBtnFn() + }, + translateLink: () => { // switch between traditional and simplified chinese + window.translateFn.translatePage() + } + } + + document.getElementById('rightside').addEventListener('click', e => { + const $target = e.target.closest('[id]') + if ($target && rightSideFn[$target.id]) { + rightSideFn[$target.id](e.currentTarget, $target) + } + }) + + /** + * menu + * 側邊欄sub-menu 展開/收縮 + */ + const clickFnOfSubMenu = () => { + const handleClickOfSubMenu = e => { + const target = e.target.closest('.site-page.group') + if (!target) return + target.classList.toggle('hide') + } + + const menusItems = document.querySelector('#sidebar-menus .menus_items') + menusItems && menusItems.addEventListener('click', handleClickOfSubMenu) + } + + /** + * 手机端目录点击 + */ + const openMobileMenu = () => { + const toggleMenu = document.getElementById('toggle-menu') + if (!toggleMenu) return + btf.addEventListenerPjax(toggleMenu, 'click', () => { sidebarFn.open() }) + } + + /** + * 複製時加上版權信息 + */ + const addCopyright = () => { + const { limitCount, languages } = GLOBAL_CONFIG.copyright + + const handleCopy = (e) => { + e.preventDefault() + const copyFont = window.getSelection(0).toString() + let textFont = copyFont + if (copyFont.length > limitCount) { + textFont = `${copyFont}\n\n\n${languages.author}\n${languages.link}${window.location.href}\n${languages.source}\n${languages.info}` + } + if (e.clipboardData) { + return e.clipboardData.setData('text', textFont) + } else { + return window.clipboardData.setData('text', textFont) + } + } + + document.body.addEventListener('copy', handleCopy) + } + + /** + * 網頁運行時間 + */ + const addRuntime = () => { + const $runtimeCount = document.getElementById('runtimeshow') + if ($runtimeCount) { + const publishDate = $runtimeCount.getAttribute('data-publishDate') + $runtimeCount.textContent = `${btf.diffDate(publishDate)} ${GLOBAL_CONFIG.runtime}` + } + } + + /** + * 最後一次更新時間 + */ + const addLastPushDate = () => { + const $lastPushDateItem = document.getElementById('last-push-date') + if ($lastPushDateItem) { + const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate') + $lastPushDateItem.textContent = btf.diffDate(lastPushDate, true) + } + } + + /** + * table overflow + */ + const addTableWrap = () => { + const $table = document.querySelectorAll('#article-container table') + if (!$table.length) return + + $table.forEach(item => { + if (!item.closest('.highlight')) { + btf.wrap(item, 'div', { class: 'table-wrap' }) + } + }) + } + + /** + * tag-hide + */ + const clickFnOfTagHide = () => { + const hideButtons = document.querySelectorAll('#article-container .hide-button') + if (!hideButtons.length) return + hideButtons.forEach(item => item.addEventListener('click', e => { + const currentTarget = e.currentTarget + currentTarget.classList.add('open') + addJustifiedGallery(currentTarget.nextElementSibling.querySelectorAll('.gallery-container')) + }, { once: true })) + } + + const tabsFn = () => { + const navTabsElements = document.querySelectorAll('#article-container .tabs') + if (!navTabsElements.length) return + + const setActiveClass = (elements, activeIndex) => { + elements.forEach((el, index) => { + el.classList.toggle('active', index === activeIndex) + }) + } + + const handleNavClick = e => { + const target = e.target.closest('button') + if (!target || target.classList.contains('active')) return + + const navItems = [...e.currentTarget.children] + const tabContents = [...e.currentTarget.nextElementSibling.children] + const indexOfButton = navItems.indexOf(target) + setActiveClass(navItems, indexOfButton) + e.currentTarget.classList.remove('no-default') + setActiveClass(tabContents, indexOfButton) + addJustifiedGallery(tabContents[indexOfButton].querySelectorAll('.gallery-container'), true) + } + + const handleToTopClick = tabElement => e => { + if (e.target.closest('button')) { + btf.scrollToDest(btf.getEleTop(tabElement), 300) + } + } + + navTabsElements.forEach(tabElement => { + btf.addEventListenerPjax(tabElement.firstElementChild, 'click', handleNavClick) + btf.addEventListenerPjax(tabElement.lastElementChild, 'click', handleToTopClick(tabElement)) + }) + } + + const toggleCardCategory = () => { + const cardCategory = document.querySelector('#aside-cat-list.expandBtn') + if (!cardCategory) return + + const handleToggleBtn = e => { + const target = e.target + if (target.nodeName === 'I') { + e.preventDefault() + target.parentNode.classList.toggle('expand') + } + } + btf.addEventListenerPjax(cardCategory, 'click', handleToggleBtn, true) + } + + const addPostOutdateNotice = () => { + const ele = document.getElementById('post-outdate-notice') + if (!ele) return + + const { limitDay, messagePrev, messageNext, postUpdate } = JSON.parse(ele.getAttribute('data')) + const diffDay = btf.diffDate(postUpdate) + if (diffDay >= limitDay) { + ele.textContent = `${messagePrev} ${diffDay} ${messageNext}` + ele.hidden = false + } + } + + const lazyloadImg = () => { + window.lazyLoadInstance = new LazyLoad({ + elements_selector: 'img', + threshold: 0, + data_src: 'lazy-src' + }) + + btf.addGlobalFn('pjaxComplete', () => { + window.lazyLoadInstance.update() + }, 'lazyload') + } + + const relativeDate = selector => { + selector.forEach(item => { + item.textContent = btf.diffDate(item.getAttribute('datetime'), true) + item.style.display = 'inline' + }) + } + + const justifiedIndexPostUI = () => { + const recentPostsElement = document.getElementById('recent-posts') + if (!(recentPostsElement && recentPostsElement.classList.contains('masonry'))) return + + const init = () => { + const masonryItem = new InfiniteGrid.MasonryInfiniteGrid('.recent-post-items', { + gap: { horizontal: 10, vertical: 20 }, + useTransform: true, + useResizeObserver: true + }) + masonryItem.renderItems() + btf.addGlobalFn('pjaxCompleteOnce', () => { masonryItem.destroy() }, 'removeJustifiedIndexPostUI') + } + + typeof InfiniteGrid === 'function' ? init() : btf.getScript(`${GLOBAL_CONFIG.infinitegrid.js}`).then(init) + } + + const unRefreshFn = () => { + window.addEventListener('resize', () => { + adjustMenu(false) + mobileSidebarOpen && btf.isHidden(document.getElementById('toggle-menu')) && sidebarFn.close() + }) + + const menuMask = document.getElementById('menu-mask') + menuMask && menuMask.addEventListener('click', () => { sidebarFn.close() }) + + clickFnOfSubMenu() + GLOBAL_CONFIG.islazyloadPlugin && lazyloadImg() + GLOBAL_CONFIG.copyright !== undefined && addCopyright() + + if (GLOBAL_CONFIG.autoDarkmode) { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { + if (btf.saveToLocal.get('theme') !== undefined) return + e.matches ? handleThemeChange('dark') : handleThemeChange('light') + }) + } + } + + const forPostFn = () => { + addHighlightTool() + addPhotoFigcaption() + addJustifiedGallery(document.querySelectorAll('#article-container .gallery-container')) + runLightbox() + scrollFnToDo() + addTableWrap() + clickFnOfTagHide() + tabsFn() + } + + const refreshFn = () => { + initAdjust() + justifiedIndexPostUI() + + if (GLOBAL_CONFIG_SITE.pageType === 'post') { + addPostOutdateNotice() + GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time')) + } else { + GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time')) + GLOBAL_CONFIG.runtime && addRuntime() + addLastPushDate() + toggleCardCategory() + } + + GLOBAL_CONFIG_SITE.pageType === 'home' && scrollDownInIndex() + scrollFn() + + forPostFn() + GLOBAL_CONFIG_SITE.pageType !== 'shuoshuo' && btf.switchComments(document) + openMobileMenu() + } + + btf.addGlobalFn('pjaxComplete', refreshFn, 'refreshFn') + refreshFn() + unRefreshFn() + + // 處理 hexo-blog-encrypt 事件 + window.addEventListener('hexo-blog-decrypt', e => { + forPostFn() + window.translateFn.translateInitialization() + Object.values(window.globalFn.encrypt).forEach(fn => { + fn() + }) + }) +}) diff --git a/js/search/algolia.js b/js/search/algolia.js new file mode 100644 index 000000000..8624f52f9 --- /dev/null +++ b/js/search/algolia.js @@ -0,0 +1,174 @@ +window.addEventListener('load', () => { + const { algolia } = GLOBAL_CONFIG + const { appId, apiKey, indexName, hitsPerPage = 5, languages } = algolia + + if (!appId || !apiKey || !indexName) { + return console.error('Algolia setting is invalid!') + } + + const $searchMask = document.getElementById('search-mask') + const $searchDialog = document.querySelector('#algolia-search .search-dialog') + + const animateElements = show => { + const action = show ? 'animateIn' : 'animateOut' + const maskAnimation = show ? 'to_show 0.5s' : 'to_hide 0.5s' + const dialogAnimation = show ? 'titleScale 0.5s' : 'search_close .5s' + btf[action]($searchMask, maskAnimation) + btf[action]($searchDialog, dialogAnimation) + } + + const fixSafariHeight = () => { + if (window.innerWidth < 768) { + $searchDialog.style.setProperty('--search-height', `${window.innerHeight}px`) + } + } + + const openSearch = () => { + btf.overflowPaddingR.add() + animateElements(true) + setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100) + + const handleEscape = event => { + if (event.code === 'Escape') { + closeSearch() + document.removeEventListener('keydown', handleEscape) + } + } + + document.addEventListener('keydown', handleEscape) + fixSafariHeight() + window.addEventListener('resize', fixSafariHeight) + } + + const closeSearch = () => { + btf.overflowPaddingR.remove() + animateElements(false) + window.removeEventListener('resize', fixSafariHeight) + } + + const searchClickFn = () => { + btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch) + } + + const searchFnOnce = () => { + $searchMask.addEventListener('click', closeSearch) + document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch) + } + + const cutContent = (content) => { + if (!content) return '' + const firstOccur = content.indexOf('') + let start = firstOccur - 30 + let end = firstOccur + 120 + let pre = '' + let post = '' + + if (start <= 0) { + start = 0 + end = 140 + } else { + pre = '...' + } + + if (end > content.length) { + end = content.length + } else { + post = '...' + } + + return `${pre}${content.substring(start, end)}${post}` + } + + const disableDiv = [ + document.getElementById('algolia-hits'), + document.getElementById('algolia-pagination'), + document.querySelector('#algolia-info .algolia-stats') + ] + + const searchClient = typeof algoliasearch === 'function' ? algoliasearch : window['algoliasearch/lite'].liteClient + const search = instantsearch({ + indexName, + searchClient: searchClient(appId, apiKey), + searchFunction (helper) { + disableDiv.forEach(item => { + item.style.display = helper.state.query ? '' : 'none' + }) + if (helper.state.query) helper.search() + } + }) + + const widgets = [ + instantsearch.widgets.configure({ hitsPerPage }), + instantsearch.widgets.searchBox({ + container: '#algolia-search-input', + showReset: false, + showSubmit: false, + placeholder: languages.input_placeholder, + showLoadingIndicator: true + }), + instantsearch.widgets.hits({ + container: '#algolia-hits', + templates: { + item (data) { + const link = data.permalink || (GLOBAL_CONFIG.root + data.path) + const result = data._highlightResult + const content = result.contentStripTruncate + ? cutContent(result.contentStripTruncate.value) + : result.contentStrip + ? cutContent(result.contentStrip.value) + : result.content + ? cutContent(result.content.value) + : '' + return ` + + ${result.title.value || 'no-title'} + ${content ? `
    ${content}
    ` : ''} +
    ` + }, + empty (data) { + return `
    ${languages.hits_empty.replace(/\$\{query}/, data.query)}
    ` + } + } + }), + instantsearch.widgets.stats({ + container: '#algolia-info > .algolia-stats', + templates: { + text (data) { + const stats = languages.hits_stats + .replace(/\$\{hits}/, data.nbHits) + .replace(/\$\{time}/, data.processingTimeMS) + return `
    ${stats}` + } + } + }), + instantsearch.widgets.poweredBy({ + container: '#algolia-info > .algolia-poweredBy' + }), + instantsearch.widgets.pagination({ + container: '#algolia-pagination', + totalPages: 5, + templates: { + first: '', + last: '', + previous: '', + next: '' + } + }) + ] + + search.addWidgets(widgets) + search.start() + searchClickFn() + searchFnOnce() + + window.addEventListener('pjax:complete', () => { + if (!btf.isHidden($searchMask)) closeSearch() + searchClickFn() + }) + + if (window.pjax) { + search.on('render', () => { + window.pjax.refresh(document.getElementById('algolia-hits')) + }) + } +}) diff --git a/js/search/local-search.js b/js/search/local-search.js new file mode 100644 index 000000000..1d3f26872 --- /dev/null +++ b/js/search/local-search.js @@ -0,0 +1,360 @@ +/** + * Refer to hexo-generator-searchdb + * https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js + * Modified by hexo-theme-butterfly + */ + +class LocalSearch { + constructor ({ + path = '', + unescape = false, + top_n_per_article = 1 + }) { + this.path = path + this.unescape = unescape + this.top_n_per_article = top_n_per_article + this.isfetched = false + this.datas = null + } + + getIndexByWord (words, text, caseSensitive = false) { + const index = [] + const included = new Set() + + if (!caseSensitive) { + text = text.toLowerCase() + } + words.forEach(word => { + if (this.unescape) { + const div = document.createElement('div') + div.innerText = word + word = div.innerHTML + } + const wordLen = word.length + if (wordLen === 0) return + let startPosition = 0 + let position = -1 + if (!caseSensitive) { + word = word.toLowerCase() + } + while ((position = text.indexOf(word, startPosition)) > -1) { + index.push({ position, word }) + included.add(word) + startPosition = position + wordLen + } + }) + // Sort index by position of keyword + index.sort((left, right) => { + if (left.position !== right.position) { + return left.position - right.position + } + return right.word.length - left.word.length + }) + return [index, included] + } + + // Merge hits into slices + mergeIntoSlice (start, end, index) { + let item = index[0] + let { position, word } = item + const hits = [] + const count = new Set() + while (position + word.length <= end && index.length !== 0) { + count.add(word) + hits.push({ + position, + length: word.length + }) + const wordEnd = position + word.length + + // Move to next position of hit + index.shift() + while (index.length !== 0) { + item = index[0] + position = item.position + word = item.word + if (wordEnd > position) { + index.shift() + } else { + break + } + } + } + return { + hits, + start, + end, + count: count.size + } + } + + // Highlight title and content + highlightKeyword (val, slice) { + let result = '' + let index = slice.start + for (const { position, length } of slice.hits) { + result += val.substring(index, position) + index = position + length + result += `${val.substr(position, length)}` + } + result += val.substring(index, slice.end) + return result + } + + getResultItems (keywords) { + const resultItems = [] + this.datas.forEach(({ title, content, url }) => { + // The number of different keywords included in the article. + const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title) + const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content) + const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size + + // Show search results + const hitCount = indexOfTitle.length + indexOfContent.length + if (hitCount === 0) return + + const slicesOfTitle = [] + if (indexOfTitle.length !== 0) { + slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle)) + } + + let slicesOfContent = [] + while (indexOfContent.length !== 0) { + const item = indexOfContent[0] + const { position } = item + // Cut out 120 characters. The maxlength of .search-input is 80. + const start = Math.max(0, position - 20) + const end = Math.min(content.length, position + 100) + slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent)) + } + + // Sort slices in content by included keywords' count and hits' count + slicesOfContent.sort((left, right) => { + if (left.count !== right.count) { + return right.count - left.count + } else if (left.hits.length !== right.hits.length) { + return right.hits.length - left.hits.length + } + return left.start - right.start + }) + + // Select top N slices in content + const upperBound = parseInt(this.top_n_per_article, 10) + if (upperBound >= 0) { + slicesOfContent = slicesOfContent.slice(0, upperBound) + } + + let resultItem = '' + + url = new URL(url, location.origin) + url.searchParams.append('highlight', keywords.join(' ')) + + if (slicesOfTitle.length !== 0) { + resultItem += `
  • ${this.highlightKeyword(title, slicesOfTitle[0])}` + } else { + resultItem += `
  • ${title}` + } + + slicesOfContent.forEach(slice => { + resultItem += `

    ${this.highlightKeyword(content, slice)}...

    ` + }) + + resultItem += '
  • ' + resultItems.push({ + item: resultItem, + id: resultItems.length, + hitCount, + includedCount + }) + }) + return resultItems + } + + fetchData () { + const isXml = !this.path.endsWith('json') + fetch(this.path) + .then(response => response.text()) + .then(res => { + // Get the contents from search data + this.isfetched = true + this.datas = isXml + ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({ + title: element.querySelector('title').textContent, + content: element.querySelector('content').textContent, + url: element.querySelector('url').textContent + })) + : JSON.parse(res) + // Only match articles with non-empty titles + this.datas = this.datas.filter(data => data.title).map(data => { + data.title = data.title.trim() + data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : '' + data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/') + return data + }) + // Remove loading animation + window.dispatchEvent(new Event('search:loaded')) + }) + } + + // Highlight by wrapping node in mark elements with the given class name + highlightText (node, slice, className) { + const val = node.nodeValue + let index = slice.start + const children = [] + for (const { position, length } of slice.hits) { + const text = document.createTextNode(val.substring(index, position)) + index = position + length + const mark = document.createElement('mark') + mark.className = className + mark.appendChild(document.createTextNode(val.substr(position, length))) + children.push(text, mark) + } + node.nodeValue = val.substring(index, slice.end) + children.forEach(element => { + node.parentNode.insertBefore(element, node) + }) + } + + // Highlight the search words provided in the url in the text + highlightSearchWords (body) { + const params = new URL(location.href).searchParams.get('highlight') + const keywords = params ? params.split(' ') : [] + if (!keywords.length || !body) return + const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null) + const allNodes = [] + while (walk.nextNode()) { + if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode) + } + allNodes.forEach(node => { + const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue) + if (!indexOfNode.length) return + const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode) + this.highlightText(node, slice, 'search-keyword') + }) + } +} + +window.addEventListener('load', () => { +// Search + const { path, top_n_per_article, unescape, languages } = GLOBAL_CONFIG.localSearch + const localSearch = new LocalSearch({ + path, + top_n_per_article, + unescape + }) + + const input = document.querySelector('#local-search-input input') + const statsItem = document.getElementById('local-search-stats-wrap') + const $loadingStatus = document.getElementById('loading-status') + const isXml = !path.endsWith('json') + + const inputEventFunction = () => { + if (!localSearch.isfetched) return + let searchText = input.value.trim().toLowerCase() + isXml && (searchText = searchText.replace(//g, '>')) + if (searchText !== '') $loadingStatus.innerHTML = '' + const keywords = searchText.split(/[-\s]+/) + const container = document.getElementById('local-search-results') + let resultItems = [] + if (searchText.length > 0) { + // Perform local searching + resultItems = localSearch.getResultItems(keywords) + } + if (keywords.length === 1 && keywords[0] === '') { + container.textContent = '' + statsItem.textContent = '' + } else if (resultItems.length === 0) { + container.textContent = '' + const statsDiv = document.createElement('div') + statsDiv.className = 'search-result-stats' + statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText) + statsItem.innerHTML = statsDiv.outerHTML + } else { + resultItems.sort((left, right) => { + if (left.includedCount !== right.includedCount) { + return right.includedCount - left.includedCount + } else if (left.hitCount !== right.hitCount) { + return right.hitCount - left.hitCount + } + return right.id - left.id + }) + + const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length) + + container.innerHTML = `
      ${resultItems.map(result => result.item).join('')}
    ` + statsItem.innerHTML = `
    ${stats}
    ` + window.pjax && window.pjax.refresh(container) + } + + $loadingStatus.textContent = '' + } + + let loadFlag = false + const $searchMask = document.getElementById('search-mask') + const $searchDialog = document.querySelector('#local-search .search-dialog') + + // fix safari + const fixSafariHeight = () => { + if (window.innerWidth < 768) { + $searchDialog.style.setProperty('--search-height', window.innerHeight + 'px') + } + } + + const openSearch = () => { + btf.overflowPaddingR.add() + btf.animateIn($searchMask, 'to_show 0.5s') + btf.animateIn($searchDialog, 'titleScale 0.5s') + setTimeout(() => { input.focus() }, 300) + if (!loadFlag) { + !localSearch.isfetched && localSearch.fetchData() + input.addEventListener('input', inputEventFunction) + loadFlag = true + } + // shortcut: ESC + document.addEventListener('keydown', function f (event) { + if (event.code === 'Escape') { + closeSearch() + document.removeEventListener('keydown', f) + } + }) + + fixSafariHeight() + window.addEventListener('resize', fixSafariHeight) + } + + const closeSearch = () => { + btf.overflowPaddingR.remove() + btf.animateOut($searchDialog, 'search_close .5s') + btf.animateOut($searchMask, 'to_hide 0.5s') + window.removeEventListener('resize', fixSafariHeight) + } + + const searchClickFn = () => { + btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch) + } + + const searchFnOnce = () => { + document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch) + $searchMask.addEventListener('click', closeSearch) + if (GLOBAL_CONFIG.localSearch.preload) { + localSearch.fetchData() + } + localSearch.highlightSearchWords(document.getElementById('article-container')) + } + + window.addEventListener('search:loaded', () => { + const $loadDataItem = document.getElementById('loading-database') + $loadDataItem.nextElementSibling.style.display = 'block' + $loadDataItem.remove() + }) + + searchClickFn() + searchFnOnce() + + // pjax + window.addEventListener('pjax:complete', () => { + !btf.isHidden($searchMask) && closeSearch() + localSearch.highlightSearchWords(document.getElementById('article-container')) + searchClickFn() + }) +}) diff --git a/js/tw_cn.js b/js/tw_cn.js new file mode 100644 index 000000000..c19d69cd9 --- /dev/null +++ b/js/tw_cn.js @@ -0,0 +1,117 @@ +document.addEventListener('DOMContentLoaded', () => { + const { defaultEncoding, translateDelay, msgToTraditionalChinese, msgToSimplifiedChinese } = GLOBAL_CONFIG.translate + const snackbarData = GLOBAL_CONFIG.Snackbar + const targetEncodingCookie = 'translate-chn-cht' + + let currentEncoding = defaultEncoding + let targetEncoding = Number(btf.saveToLocal.get(targetEncodingCookie)) || defaultEncoding + const translateButtonObject = document.getElementById('translateLink') + const isSnackbar = snackbarData !== undefined + + const setLang = () => { + document.documentElement.lang = targetEncoding === 1 ? 'zh-TW' : 'zh-CN' + } + + const translateText = (txt) => { + if (!txt) return '' + if (currentEncoding === 1 && targetEncoding === 2) return Simplized(txt) + if (currentEncoding === 2 && targetEncoding === 1) return Traditionalized(txt) + return txt + } + + const translateBody = (fobj) => { + const nodes = typeof fobj === 'object' ? fobj.childNodes : document.body.childNodes + + for (const node of nodes) { + // Skip BR, HR tags, or the translate button object + if (['BR', 'HR'].includes(node.tagName) || node === translateButtonObject) continue + + if (node.nodeType === Node.ELEMENT_NODE) { + const { tagName, title, alt, placeholder, value, type } = node + + // Translate title, alt, placeholder + if (title) node.title = translateText(title) + if (alt) node.alt = translateText(alt) + if (placeholder) node.placeholder = translateText(placeholder) + + // Translate input value except text and hidden types + if (tagName === 'INPUT' && value && type !== 'text' && type !== 'hidden') { + node.value = translateText(value) + } + + // Recursively translate child nodes + translateBody(node) + } else if (node.nodeType === Node.TEXT_NODE) { + // Translate text node data + node.data = translateText(node.data) + } + } + } + + const translatePage = () => { + if (targetEncoding === 1) { + currentEncoding = 1 + targetEncoding = 2 + translateButtonObject.textContent = msgToTraditionalChinese + isSnackbar && btf.snackbarShow(snackbarData.cht_to_chs) + } else if (targetEncoding === 2) { + currentEncoding = 2 + targetEncoding = 1 + translateButtonObject.textContent = msgToSimplifiedChinese + isSnackbar && btf.snackbarShow(snackbarData.chs_to_cht) + } + btf.saveToLocal.set(targetEncodingCookie, targetEncoding, 2) + setLang() + translateBody() + } + + const JTPYStr = () => '万与丑专业丛东丝丢两严丧个丬丰临为丽举么义乌乐乔习乡书买乱争于亏云亘亚产亩亲亵亸亿仅从仑仓仪们价众优伙会伛伞伟传伤伥伦伧伪伫体余佣佥侠侣侥侦侧侨侩侪侬俣俦俨俩俪俭债倾偬偻偾偿傥傧储傩儿兑兖党兰关兴兹养兽冁内冈册写军农冢冯冲决况冻净凄凉凌减凑凛几凤凫凭凯击凼凿刍划刘则刚创删别刬刭刽刿剀剂剐剑剥剧劝办务劢动励劲劳势勋勐勚匀匦匮区医华协单卖卢卤卧卫却卺厂厅历厉压厌厍厕厢厣厦厨厩厮县参叆叇双发变叙叠叶号叹叽吁后吓吕吗吣吨听启吴呒呓呕呖呗员呙呛呜咏咔咙咛咝咤咴咸哌响哑哒哓哔哕哗哙哜哝哟唛唝唠唡唢唣唤唿啧啬啭啮啰啴啸喷喽喾嗫呵嗳嘘嘤嘱噜噼嚣嚯团园囱围囵国图圆圣圹场坂坏块坚坛坜坝坞坟坠垄垅垆垒垦垧垩垫垭垯垱垲垴埘埙埚埝埯堑堕塆墙壮声壳壶壸处备复够头夸夹夺奁奂奋奖奥妆妇妈妩妪妫姗姜娄娅娆娇娈娱娲娴婳婴婵婶媪嫒嫔嫱嬷孙学孪宁宝实宠审宪宫宽宾寝对寻导寿将尔尘尧尴尸尽层屃屉届属屡屦屿岁岂岖岗岘岙岚岛岭岳岽岿峃峄峡峣峤峥峦崂崃崄崭嵘嵚嵛嵝嵴巅巩巯币帅师帏帐帘帜带帧帮帱帻帼幂幞干并广庄庆庐庑库应庙庞废庼廪开异弃张弥弪弯弹强归当录彟彦彻径徕御忆忏忧忾怀态怂怃怄怅怆怜总怼怿恋恳恶恸恹恺恻恼恽悦悫悬悭悯惊惧惨惩惫惬惭惮惯愍愠愤愦愿慑慭憷懑懒懔戆戋戏戗战戬户扎扑扦执扩扪扫扬扰抚抛抟抠抡抢护报担拟拢拣拥拦拧拨择挂挚挛挜挝挞挟挠挡挢挣挤挥挦捞损捡换捣据捻掳掴掷掸掺掼揸揽揿搀搁搂搅携摄摅摆摇摈摊撄撑撵撷撸撺擞攒敌敛数斋斓斗斩断无旧时旷旸昙昼昽显晋晒晓晔晕晖暂暧札术朴机杀杂权条来杨杩杰极构枞枢枣枥枧枨枪枫枭柜柠柽栀栅标栈栉栊栋栌栎栏树栖样栾桊桠桡桢档桤桥桦桧桨桩梦梼梾检棂椁椟椠椤椭楼榄榇榈榉槚槛槟槠横樯樱橥橱橹橼檐檩欢欤欧歼殁殇残殒殓殚殡殴毁毂毕毙毡毵氇气氢氩氲汇汉污汤汹沓沟没沣沤沥沦沧沨沩沪沵泞泪泶泷泸泺泻泼泽泾洁洒洼浃浅浆浇浈浉浊测浍济浏浐浑浒浓浔浕涂涌涛涝涞涟涠涡涢涣涤润涧涨涩淀渊渌渍渎渐渑渔渖渗温游湾湿溃溅溆溇滗滚滞滟滠满滢滤滥滦滨滩滪漤潆潇潋潍潜潴澜濑濒灏灭灯灵灾灿炀炉炖炜炝点炼炽烁烂烃烛烟烦烧烨烩烫烬热焕焖焘煅煳熘爱爷牍牦牵牺犊犟状犷犸犹狈狍狝狞独狭狮狯狰狱狲猃猎猕猡猪猫猬献獭玑玙玚玛玮环现玱玺珉珏珐珑珰珲琎琏琐琼瑶瑷璇璎瓒瓮瓯电画畅畲畴疖疗疟疠疡疬疮疯疱疴痈痉痒痖痨痪痫痴瘅瘆瘗瘘瘪瘫瘾瘿癞癣癫癯皑皱皲盏盐监盖盗盘眍眦眬着睁睐睑瞒瞩矫矶矾矿砀码砖砗砚砜砺砻砾础硁硅硕硖硗硙硚确硷碍碛碜碱碹磙礼祎祢祯祷祸禀禄禅离秃秆种积称秽秾稆税稣稳穑穷窃窍窑窜窝窥窦窭竖竞笃笋笔笕笺笼笾筑筚筛筜筝筹签简箓箦箧箨箩箪箫篑篓篮篱簖籁籴类籼粜粝粤粪粮糁糇紧絷纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵罂网罗罚罢罴羁羟羡翘翙翚耢耧耸耻聂聋职聍联聩聪肃肠肤肷肾肿胀胁胆胜胧胨胪胫胶脉脍脏脐脑脓脔脚脱脶脸腊腌腘腭腻腼腽腾膑臜舆舣舰舱舻艰艳艹艺节芈芗芜芦苁苇苈苋苌苍苎苏苘苹茎茏茑茔茕茧荆荐荙荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药莅莜莱莲莳莴莶获莸莹莺莼萚萝萤营萦萧萨葱蒇蒉蒋蒌蓝蓟蓠蓣蓥蓦蔷蔹蔺蔼蕲蕴薮藁藓虏虑虚虫虬虮虽虾虿蚀蚁蚂蚕蚝蚬蛊蛎蛏蛮蛰蛱蛲蛳蛴蜕蜗蜡蝇蝈蝉蝎蝼蝾螀螨蟏衅衔补衬衮袄袅袆袜袭袯装裆裈裢裣裤裥褛褴襁襕见观觃规觅视觇览觉觊觋觌觍觎觏觐觑觞触觯詟誉誊讠计订讣认讥讦讧讨让讪讫训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豮贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赪赵赶趋趱趸跃跄跖跞践跶跷跸跹跻踊踌踪踬踯蹑蹒蹰蹿躏躜躯车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辞辩辫边辽达迁过迈运还这进远违连迟迩迳迹适选逊递逦逻遗遥邓邝邬邮邹邺邻郁郄郏郐郑郓郦郧郸酝酦酱酽酾酿释里鉅鉴銮錾钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铍铎铏铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗错锚锜锞锟锠锡锢锣锤锥锦锨锩锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镆镇镈镉镊镌镍镎镏镐镑镒镕镖镗镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镶长门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛队阳阴阵阶际陆陇陈陉陕陧陨险随隐隶隽难雏雠雳雾霁霉霭靓静靥鞑鞒鞯鞴韦韧韨韩韪韫韬韵页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧风飏飐飑飒飓飔飕飖飗飘飙飚飞飨餍饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧髅髋髌鬓魇魉鱼鱽鱾鱿鲀鲁鲂鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳛鳜鳝鳞鳟鳠鳡鳢鳣鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹯鹰鹱鹲鹳鹴鹾麦麸黄黉黡黩黪黾龙历志制一台皋准复猛钟注范签' + const FTPYStr = () => '萬與醜專業叢東絲丟兩嚴喪個爿豐臨為麗舉麼義烏樂喬習鄉書買亂爭於虧雲亙亞產畝親褻嚲億僅從侖倉儀們價眾優夥會傴傘偉傳傷倀倫傖偽佇體餘傭僉俠侶僥偵側僑儈儕儂俁儔儼倆儷儉債傾傯僂僨償儻儐儲儺兒兌兗黨蘭關興茲養獸囅內岡冊寫軍農塚馮衝決況凍淨淒涼淩減湊凜幾鳳鳧憑凱擊氹鑿芻劃劉則剛創刪別剗剄劊劌剴劑剮劍剝劇勸辦務勱動勵勁勞勢勳猛勩勻匭匱區醫華協單賣盧鹵臥衛卻巹廠廳曆厲壓厭厙廁廂厴廈廚廄廝縣參靉靆雙發變敘疊葉號歎嘰籲後嚇呂嗎唚噸聽啟吳嘸囈嘔嚦唄員咼嗆嗚詠哢嚨嚀噝吒噅鹹呱響啞噠嘵嗶噦嘩噲嚌噥喲嘜嗊嘮啢嗩唕喚呼嘖嗇囀齧囉嘽嘯噴嘍嚳囁嗬噯噓嚶囑嚕劈囂謔團園囪圍圇國圖圓聖壙場阪壞塊堅壇壢壩塢墳墜壟壟壚壘墾坰堊墊埡墶壋塏堖塒塤堝墊垵塹墮壪牆壯聲殼壺壼處備複夠頭誇夾奪奩奐奮獎奧妝婦媽嫵嫗媯姍薑婁婭嬈嬌孌娛媧嫻嫿嬰嬋嬸媼嬡嬪嬙嬤孫學孿寧寶實寵審憲宮寬賓寢對尋導壽將爾塵堯尷屍盡層屭屜屆屬屢屨嶼歲豈嶇崗峴嶴嵐島嶺嶽崠巋嶨嶧峽嶢嶠崢巒嶗崍嶮嶄嶸嶔崳嶁脊巔鞏巰幣帥師幃帳簾幟帶幀幫幬幘幗冪襆幹並廣莊慶廬廡庫應廟龐廢廎廩開異棄張彌弳彎彈強歸當錄彠彥徹徑徠禦憶懺憂愾懷態慫憮慪悵愴憐總懟懌戀懇惡慟懨愷惻惱惲悅愨懸慳憫驚懼慘懲憊愜慚憚慣湣慍憤憒願懾憖怵懣懶懍戇戔戲戧戰戩戶紮撲扡執擴捫掃揚擾撫拋摶摳掄搶護報擔擬攏揀擁攔擰撥擇掛摯攣掗撾撻挾撓擋撟掙擠揮撏撈損撿換搗據撚擄摑擲撣摻摜摣攬撳攙擱摟攪攜攝攄擺搖擯攤攖撐攆擷擼攛擻攢敵斂數齋斕鬥斬斷無舊時曠暘曇晝曨顯晉曬曉曄暈暉暫曖劄術樸機殺雜權條來楊榪傑極構樅樞棗櫪梘棖槍楓梟櫃檸檉梔柵標棧櫛櫳棟櫨櫟欄樹棲樣欒棬椏橈楨檔榿橋樺檜槳樁夢檮棶檢欞槨櫝槧欏橢樓欖櫬櫚櫸檟檻檳櫧橫檣櫻櫫櫥櫓櫞簷檁歡歟歐殲歿殤殘殞殮殫殯毆毀轂畢斃氈毿氌氣氫氬氳彙漢汙湯洶遝溝沒灃漚瀝淪滄渢溈滬濔濘淚澩瀧瀘濼瀉潑澤涇潔灑窪浹淺漿澆湞溮濁測澮濟瀏滻渾滸濃潯濜塗湧濤澇淶漣潿渦溳渙滌潤澗漲澀澱淵淥漬瀆漸澠漁瀋滲溫遊灣濕潰濺漵漊潷滾滯灩灄滿瀅濾濫灤濱灘澦濫瀠瀟瀲濰潛瀦瀾瀨瀕灝滅燈靈災燦煬爐燉煒熗點煉熾爍爛烴燭煙煩燒燁燴燙燼熱煥燜燾煆糊溜愛爺牘犛牽犧犢強狀獷獁猶狽麅獮獰獨狹獅獪猙獄猻獫獵獼玀豬貓蝟獻獺璣璵瑒瑪瑋環現瑲璽瑉玨琺瓏璫琿璡璉瑣瓊瑤璦璿瓔瓚甕甌電畫暢佘疇癤療瘧癘瘍鬁瘡瘋皰屙癰痙癢瘂癆瘓癇癡癉瘮瘞瘺癟癱癮癭癩癬癲臒皚皺皸盞鹽監蓋盜盤瞘眥矓著睜睞瞼瞞矚矯磯礬礦碭碼磚硨硯碸礪礱礫礎硜矽碩硤磽磑礄確鹼礙磧磣堿镟滾禮禕禰禎禱禍稟祿禪離禿稈種積稱穢穠穭稅穌穩穡窮竊竅窯竄窩窺竇窶豎競篤筍筆筧箋籠籩築篳篩簹箏籌簽簡籙簀篋籜籮簞簫簣簍籃籬籪籟糴類秈糶糲粵糞糧糝餱緊縶糸糾紆紅紂纖紇約級紈纊紀紉緯紜紘純紕紗綱納紝縱綸紛紙紋紡紵紖紐紓線紺絏紱練組紳細織終縐絆紼絀紹繹經紿綁絨結絝繞絰絎繪給絢絳絡絕絞統綆綃絹繡綌綏絛繼綈績緒綾緓續綺緋綽緔緄繩維綿綬繃綢綯綹綣綜綻綰綠綴緇緙緗緘緬纜緹緲緝縕繢緦綞緞緶線緱縋緩締縷編緡緣縉縛縟縝縫縗縞纏縭縊縑繽縹縵縲纓縮繆繅纈繚繕繒韁繾繰繯繳纘罌網羅罰罷羆羈羥羨翹翽翬耮耬聳恥聶聾職聹聯聵聰肅腸膚膁腎腫脹脅膽勝朧腖臚脛膠脈膾髒臍腦膿臠腳脫腡臉臘醃膕齶膩靦膃騰臏臢輿艤艦艙艫艱豔艸藝節羋薌蕪蘆蓯葦藶莧萇蒼苧蘇檾蘋莖蘢蔦塋煢繭荊薦薘莢蕘蓽蕎薈薺蕩榮葷滎犖熒蕁藎蓀蔭蕒葒葤藥蒞蓧萊蓮蒔萵薟獲蕕瑩鶯蓴蘀蘿螢營縈蕭薩蔥蕆蕢蔣蔞藍薊蘺蕷鎣驀薔蘞藺藹蘄蘊藪槁蘚虜慮虛蟲虯蟣雖蝦蠆蝕蟻螞蠶蠔蜆蠱蠣蟶蠻蟄蛺蟯螄蠐蛻蝸蠟蠅蟈蟬蠍螻蠑螿蟎蠨釁銜補襯袞襖嫋褘襪襲襏裝襠褌褳襝褲襇褸襤繈襴見觀覎規覓視覘覽覺覬覡覿覥覦覯覲覷觴觸觶讋譽謄訁計訂訃認譏訐訌討讓訕訖訓議訊記訒講諱謳詎訝訥許訛論訩訟諷設訪訣證詁訶評詛識詗詐訴診詆謅詞詘詔詖譯詒誆誄試詿詩詰詼誠誅詵話誕詬詮詭詢詣諍該詳詫諢詡譸誡誣語誚誤誥誘誨誑說誦誒請諸諏諾讀諑誹課諉諛誰諗調諂諒諄誶談誼謀諶諜謊諫諧謔謁謂諤諭諼讒諮諳諺諦謎諞諝謨讜謖謝謠謗諡謙謐謹謾謫譾謬譚譖譙讕譜譎讞譴譫讖穀豶貝貞負貟貢財責賢敗賬貨質販貪貧貶購貯貫貳賤賁貰貼貴貺貸貿費賀貽賊贄賈賄貲賃賂贓資賅贐賕賑賚賒賦賭齎贖賞賜贔賙賡賠賧賴賵贅賻賺賽賾贗讚贇贈贍贏贛赬趙趕趨趲躉躍蹌蹠躒踐躂蹺蹕躚躋踴躊蹤躓躑躡蹣躕躥躪躦軀車軋軌軒軑軔轉軛輪軟轟軲軻轤軸軹軼軤軫轢軺輕軾載輊轎輈輇輅較輒輔輛輦輩輝輥輞輬輟輜輳輻輯轀輸轡轅轄輾轆轍轔辭辯辮邊遼達遷過邁運還這進遠違連遲邇逕跡適選遜遞邐邏遺遙鄧鄺鄔郵鄒鄴鄰鬱郤郟鄶鄭鄆酈鄖鄲醞醱醬釅釃釀釋裏钜鑒鑾鏨釓釔針釘釗釙釕釷釺釧釤鈒釩釣鍆釹鍚釵鈃鈣鈈鈦鈍鈔鍾鈉鋇鋼鈑鈐鑰欽鈞鎢鉤鈧鈁鈥鈄鈕鈀鈺錢鉦鉗鈷缽鈳鉕鈽鈸鉞鑽鉬鉭鉀鈿鈾鐵鉑鈴鑠鉛鉚鈰鉉鉈鉍鈹鐸鉶銬銠鉺銪鋏鋣鐃銍鐺銅鋁銱銦鎧鍘銖銑鋌銩銛鏵銓鉿銚鉻銘錚銫鉸銥鏟銃鐋銨銀銣鑄鐒鋪鋙錸鋱鏈鏗銷鎖鋰鋥鋤鍋鋯鋨鏽銼鋝鋒鋅鋶鐦鐧銳銻鋃鋟鋦錒錆鍺錯錨錡錁錕錩錫錮鑼錘錐錦鍁錈錇錟錠鍵鋸錳錙鍥鍈鍇鏘鍶鍔鍤鍬鍾鍛鎪鍠鍰鎄鍍鎂鏤鎡鏌鎮鎛鎘鑷鐫鎳鎿鎦鎬鎊鎰鎔鏢鏜鏍鏰鏞鏡鏑鏃鏇鏐鐔钁鐐鏷鑥鐓鑭鐠鑹鏹鐙鑊鐳鐶鐲鐮鐿鑔鑣鑞鑲長門閂閃閆閈閉問闖閏闈閑閎間閔閌悶閘鬧閨聞闥閩閭闓閥閣閡閫鬮閱閬闍閾閹閶鬩閿閽閻閼闡闌闃闠闊闋闔闐闒闕闞闤隊陽陰陣階際陸隴陳陘陝隉隕險隨隱隸雋難雛讎靂霧霽黴靄靚靜靨韃鞽韉韝韋韌韍韓韙韞韜韻頁頂頃頇項順須頊頑顧頓頎頒頌頏預顱領頗頸頡頰頲頜潁熲頦頤頻頮頹頷頴穎顆題顒顎顓顏額顳顢顛顙顥纇顫顬顰顴風颺颭颮颯颶颸颼颻飀飄飆飆飛饗饜飣饑飥餳飩餼飪飫飭飯飲餞飾飽飼飿飴餌饒餉餄餎餃餏餅餑餖餓餘餒餕餜餛餡館餷饋餶餿饞饁饃餺餾饈饉饅饊饌饢馬馭馱馴馳驅馹駁驢駔駛駟駙駒騶駐駝駑駕驛駘驍罵駰驕驊駱駭駢驫驪騁驗騂駸駿騏騎騍騅騌驌驂騙騭騤騷騖驁騮騫騸驃騾驄驏驟驥驦驤髏髖髕鬢魘魎魚魛魢魷魨魯魴魺鮁鮃鯰鱸鮋鮓鮒鮊鮑鱟鮍鮐鮭鮚鮳鮪鮞鮦鰂鮜鱠鱭鮫鮮鮺鯗鱘鯁鱺鰱鰹鯉鰣鰷鯀鯊鯇鮶鯽鯒鯖鯪鯕鯫鯡鯤鯧鯝鯢鯰鯛鯨鯵鯴鯔鱝鰈鰏鱨鯷鰮鰃鰓鱷鰍鰒鰉鰁鱂鯿鰠鼇鰭鰨鰥鰩鰟鰜鰳鰾鱈鱉鰻鰵鱅鰼鱖鱔鱗鱒鱯鱤鱧鱣鳥鳩雞鳶鳴鳲鷗鴉鶬鴇鴆鴣鶇鸕鴨鴞鴦鴒鴟鴝鴛鴬鴕鷥鷙鴯鴰鵂鴴鵃鴿鸞鴻鵐鵓鸝鵑鵠鵝鵒鷳鵜鵡鵲鶓鵪鶤鵯鵬鵮鶉鶊鵷鷫鶘鶡鶚鶻鶿鶥鶩鷊鷂鶲鶹鶺鷁鶼鶴鷖鸚鷓鷚鷯鷦鷲鷸鷺鸇鷹鸌鸏鸛鸘鹺麥麩黃黌黶黷黲黽龍歷誌製壹臺臯準復勐鐘註範籤' + + const Traditionalized = (cc) => { + let str = '' + const ss = JTPYStr() + const tt = FTPYStr() + for (let i = 0; i < cc.length; i++) { + if (cc.charCodeAt(i) > 10000 && ss.indexOf(cc.charAt(i)) !== -1) { + str += tt.charAt(ss.indexOf(cc.charAt(i))) + } else str += cc.charAt(i) + } + return str + } + + const Simplized = (cc) => { + let str = '' + const ss = JTPYStr() + const tt = FTPYStr() + for (let i = 0; i < cc.length; i++) { + if (cc.charCodeAt(i) > 10000 && tt.indexOf(cc.charAt(i)) !== -1) { + str += ss.charAt(tt.indexOf(cc.charAt(i))) + } else str += cc.charAt(i) + } + return str + } + + const translateInitialization = () => { + if (translateButtonObject) { + if (currentEncoding !== targetEncoding) { + translateButtonObject.textContent = + targetEncoding === 1 + ? msgToSimplifiedChinese + : msgToTraditionalChinese + setLang() + setTimeout(translateBody, translateDelay) + } + } + } + + window.translateFn = { + translatePage, + Traditionalized, + Simplized, + translateInitialization + } + + translateInitialization() + btf.addGlobalFn('pjaxComplete', translateInitialization, 'translateInitialization') +}) diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 000000000..48d8306d0 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,313 @@ +(() => { + const btfFn = { + debounce: (func, wait = 0, immediate = false) => { + let timeout + return (...args) => { + const later = () => { + timeout = null + if (!immediate) func(...args) + } + const callNow = immediate && !timeout + clearTimeout(timeout) + timeout = setTimeout(later, wait) + if (callNow) func(...args) + } + }, + + throttle: function (func, wait, options = {}) { + let timeout, context, args + let previous = 0 + + const later = () => { + previous = options.leading === false ? 0 : new Date().getTime() + timeout = null + func.apply(context, args) + if (!timeout) context = args = null + } + + const throttled = (...params) => { + const now = new Date().getTime() + if (!previous && options.leading === false) previous = now + const remaining = wait - (now - previous) + context = this + args = params + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout) + timeout = null + } + previous = now + func.apply(context, args) + if (!timeout) context = args = null + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining) + } + } + + return throttled + }, + + overflowPaddingR: { + add: () => { + const paddingRight = window.innerWidth - document.body.clientWidth + + if (paddingRight > 0) { + document.body.style.paddingRight = `${paddingRight}px` + document.body.style.overflow = 'hidden' + const menuElement = document.querySelector('#page-header.nav-fixed #menus') + if (menuElement) { + menuElement.style.paddingRight = `${paddingRight}px` + } + } + }, + remove: () => { + document.body.style.paddingRight = '' + document.body.style.overflow = '' + const menuElement = document.querySelector('#page-header.nav-fixed #menus') + if (menuElement) { + menuElement.style.paddingRight = '' + } + } + }, + + snackbarShow: (text, showAction = false, duration = 2000) => { + const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar + const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark + Snackbar.show({ + text, + backgroundColor: bg, + showAction, + duration, + pos: position, + customClass: 'snackbar-css' + }) + }, + + diffDate: (inputDate, more = false) => { + const dateNow = new Date() + const datePost = new Date(inputDate) + const diffMs = dateNow - datePost + const diffSec = diffMs / 1000 + const diffMin = diffSec / 60 + const diffHour = diffMin / 60 + const diffDay = diffHour / 24 + const diffMonth = diffDay / 30 + const { dateSuffix } = GLOBAL_CONFIG + + if (!more) return Math.floor(diffDay) + + if (diffMonth > 12) return datePost.toISOString().slice(0, 10) + if (diffMonth >= 1) return `${Math.floor(diffMonth)} ${dateSuffix.month}` + if (diffDay >= 1) return `${Math.floor(diffDay)} ${dateSuffix.day}` + if (diffHour >= 1) return `${Math.floor(diffHour)} ${dateSuffix.hour}` + if (diffMin >= 1) return `${Math.floor(diffMin)} ${dateSuffix.min}` + return dateSuffix.just + }, + + loadComment: (dom, callback) => { + if ('IntersectionObserver' in window) { + const observerItem = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting) { + callback() + observerItem.disconnect() + } + }, { threshold: [0] }) + observerItem.observe(dom) + } else { + callback() + } + }, + + scrollToDest: (pos, time = 500) => { + const currentPos = window.scrollY + const isNavFixed = document.getElementById('page-header').classList.contains('fixed') + if (currentPos > pos || isNavFixed) pos = pos - 70 + + if ('scrollBehavior' in document.documentElement.style) { + window.scrollTo({ + top: pos, + behavior: 'smooth' + }) + return + } + + const startTime = performance.now() + const animate = currentTime => { + const timeElapsed = currentTime - startTime + const progress = Math.min(timeElapsed / time, 1) + window.scrollTo(0, currentPos + (pos - currentPos) * progress) + if (progress < 1) { + requestAnimationFrame(animate) + } + } + requestAnimationFrame(animate) + }, + + animateIn: (ele, animation) => { + ele.style.display = 'block' + ele.style.animation = animation + }, + + animateOut: (ele, animation) => { + const handleAnimationEnd = () => { + ele.style.display = '' + ele.style.animation = '' + ele.removeEventListener('animationend', handleAnimationEnd) + } + ele.addEventListener('animationend', handleAnimationEnd) + ele.style.animation = animation + }, + + wrap: (selector, eleType, options) => { + const createEle = document.createElement(eleType) + for (const [key, value] of Object.entries(options)) { + createEle.setAttribute(key, value) + } + selector.parentNode.insertBefore(createEle, selector) + createEle.appendChild(selector) + }, + + isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0, + + getEleTop: ele => { + let actualTop = ele.offsetTop + let current = ele.offsetParent + + while (current !== null) { + actualTop += current.offsetTop + current = current.offsetParent + } + + return actualTop + }, + + loadLightbox: ele => { + const service = GLOBAL_CONFIG.lightbox + + if (service === 'medium_zoom') { + mediumZoom(ele, { background: 'var(--zoom-bg)' }) + } + + if (service === 'fancybox') { + Array.from(ele).forEach(i => { + if (i.parentNode.tagName !== 'A') { + const dataSrc = i.dataset.lazySrc || i.src + const dataCaption = i.title || i.alt || '' + btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc }) + } + }) + + if (!window.fancyboxRun) { + Fancybox.bind('[data-fancybox]', { + Hash: false, + Thumbs: { + showOnStart: false + }, + Images: { + Panzoom: { + maxScale: 4 + } + }, + Carousel: { + transition: 'slide' + }, + Toolbar: { + display: { + left: ['infobar'], + middle: [ + 'zoomIn', + 'zoomOut', + 'toggle1to1', + 'rotateCCW', + 'rotateCW', + 'flipX', + 'flipY' + ], + right: ['slideshow', 'thumbs', 'close'] + } + } + }) + window.fancyboxRun = true + } + } + }, + + setLoading: { + add: ele => { + const html = ` +
    +
    +
    +
    +
    + ` + ele.insertAdjacentHTML('afterend', html) + }, + remove: ele => { + ele.nextElementSibling.remove() + } + }, + + updateAnchor: anchor => { + if (anchor !== window.location.hash) { + if (!anchor) anchor = location.pathname + const title = GLOBAL_CONFIG_SITE.title + window.history.replaceState({ + url: location.href, + title + }, title, anchor) + } + }, + + getScrollPercent: (() => { + let docHeight, winHeight, headerHeight, contentMath + + return (currentTop, ele) => { + if (!docHeight || ele.clientHeight !== docHeight) { + docHeight = ele.clientHeight + winHeight = window.innerHeight + headerHeight = ele.offsetTop + contentMath = Math.max(docHeight - winHeight, document.documentElement.scrollHeight - winHeight) + } + + const scrollPercent = (currentTop - headerHeight) / contentMath + return Math.max(0, Math.min(100, Math.round(scrollPercent * 100))) + } + })(), + + addEventListenerPjax: (ele, event, fn, option = false) => { + ele.addEventListener(event, fn, option) + btf.addGlobalFn('pjaxSendOnce', () => { + ele.removeEventListener(event, fn, option) + }) + }, + + removeGlobalFnEvent: (key, parent = window) => { + const globalFn = parent.globalFn || {} + const keyObj = globalFn[key] + if (!keyObj) return + + Object.keys(keyObj).forEach(i => keyObj[i]()) + + delete globalFn[key] + }, + + switchComments: (el = document, path) => { + const switchBtn = el.querySelector('#switch-btn') + if (!switchBtn) return + + let switchDone = false + const postComment = el.querySelector('#post-comment') + const handleSwitchBtn = () => { + postComment.classList.toggle('move') + if (!switchDone && typeof loadOtherComment === 'function') { + switchDone = true + loadOtherComment(el, path) + } + } + btf.addEventListenerPjax(switchBtn, 'click', handleSwitchBtn) + } + } + + window.btf = { ...window.btf, ...btfFn } +})() diff --git a/package.json b/package.json deleted file mode 100644 index 8651cb7bc..000000000 --- a/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "lee-blog", - "version": "0.0.2", - "private": true, - "scripts": { - "clean": "hexo clean", - "build": "hexo generate", - "deploy": "hexo deploy", - "serve": "hexo server" - }, - "hexo": { - "version": "5.4.2" - }, - "dependencies": { - "hexo": "^5.2.0", - "hexo-asset-image": "^1.0.0", - "hexo-deployer-git": "^2.1.0", - "hexo-generator-archive": "^1.0.0", - "hexo-generator-category": "^1.0.0", - "hexo-generator-index": "^2.0.0", - "hexo-generator-tag": "^1.0.0", - "hexo-renderer-ejs": "^1.0.0", - "hexo-renderer-marked": "^3.3.0", - "hexo-renderer-pug": "^1.0.0", - "hexo-renderer-stylus": "^2.0.1", - "hexo-server": "^2.0.0", - "hexo-wordcount": "^6.0.1" - } -} \ No newline at end of file diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 000000000..7a4fbf1f6 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,237 @@ +淳淳同学的个人博客 - 人不可一日无事 + + + + + + + + + +
    linear-gradient函数实现线性渐变
    navigator.userAgent获取当前设备信息
    实现输入框的n种形式
    viewerjs查看大图组件
    修改element-ui的全局配置
    安卓微信浏览器中type=file的input框无法使用
    copy-webpack-plugin处理单独js文件
    vue3的生命周期钩子
    git仓库推送脚本(本地简易版)
    qiankun的css样式污染解决方案
    \ No newline at end of file diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 000000000..c991793f9 --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,237 @@ +淳淳同学的个人博客 - 人不可一日无事 + + + + + + + + + +
    GitHub Corners
    git贡献墙 gitcalendar
    Mac配置多个SSH-Key
    node打包内存溢出
    git推送后没有贡献记录
    jsdelivr
    中央事件总线插件vue-bus-ts
    qiankun 2.0.24 爬坑记录
    2020前端面试
    你不知道的JavaScript(上卷)
    \ No newline at end of file diff --git a/page/4/index.html b/page/4/index.html new file mode 100644 index 000000000..464ebbe67 --- /dev/null +++ b/page/4/index.html @@ -0,0 +1,237 @@ +淳淳同学的个人博客 - 人不可一日无事 + + + + + + + + + +
    Ubuntu 16.04 部署命令
    LeetCode常见题
    《前端内参》读书笔记
    前端个人学习笔记与项目规范
    程序员进修文档
    js-md5
    从0开始搭建Hexo个人博客
    \ No newline at end of file diff --git a/push.sh b/push.sh deleted file mode 100644 index a74ad3611..000000000 --- a/push.sh +++ /dev/null @@ -1,27 +0,0 @@ -# echo → fetch最新代码 -# git fetch - -# echo → 将最新代码pull到本地 -# git pull - -read -p "→ 请输入您的commit提交信息:" MSG - -echo → 暂存选取所有代码 -git add . - -echo → 提交所有暂存代码 -git commit -m "$MSG" - -echo → 将代码推送至三端git仓库 -git push -u all hexo - -# 复制文件内容 -# cp -r -f /Users/qducc/Documents/0_blog_posts/ /Users/qducc/Qducc/blog/source/_posts - -# 删除 9_blog 文件夹下的所有内容 -# rm -rf /Users/qducc/Documents/0_blog_posts/* - -echo → Hexo自动构建及部署 -npm run clean -npm run build -npm run deploy \ No newline at end of file diff --git a/release.sh b/release.sh deleted file mode 100644 index b15d6f611..000000000 --- a/release.sh +++ /dev/null @@ -1,4 +0,0 @@ -echo → Hexo自动构建及部署 -npm run clean -npm run build -npm run deploy \ No newline at end of file diff --git a/resume/index.html b/resume/index.html new file mode 100644 index 000000000..28676cbd1 --- /dev/null +++ b/resume/index.html @@ -0,0 +1,392 @@ +个人简历 | 淳淳同学的个人博客 + + + + + + + + + + + +

    壹。 自我介绍

    个人信息

      +
    • 头像:
    • +
    • 个人头像
    • +
    • 姓名:李淳淳
    • +
    • 求职意向:前端开发想转全栈开发(对大数据、人工智能感兴趣)
    • +
    • 联系方式:15610045821
    • +
    • 电子邮箱:961150665@qq.com
    • +
    • 个人博客:淳淳同学的个人博客
    • +
    +

    技能掌握

      +
    • 熟练运用:vue2.x、vue3.x、Element Plus、Vuex、Vue-Router、…
    • +
    • 灵活使用:Webpack、qiankun、TypeScript、Vite、Bus、微信小程序…
    • +
    • 额外了解:Electron、Django + Restful Api、Taro、Uni-App、…
    • +
    +

    教育经历

      +
    • 专业硕士研究生(非全日制),青岛大学(2018.9-2021.7),计算机技术;
    • +
    • 本科,青岛理工大学(2014.9-2018.7),网络工程;
    • +
    +

    个人评价

      +
    • 坚持6年相声演出;坚持3年每天写日记;
    • +
    • 喜欢研究阅读源码;喜欢对新技术落地实施;
    • +
    • 细节把握成败;勇于犯错且敢于承担责任;
    • +
    • 性格外向开朗;善于团队合作;
    • +
    +

    贰。 项目详细介绍

    V7联络云 - 访客端(PC + H5)

    从2020-12-01至今历时7个月,已迭代至V3.4.2版本。

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    gantt

    title 进度周期(甘特图)
    dateFormat YYYY-MM-DD

    section 业务逻辑
    需求评审 :a1, 2020-12-01, 20d
    V1.0 :a2, 2020-12-22, 66d
    V2.0 :a3, 2021-03-01, 45d
    V3.0 :a4, 2021-04-18, 70d
    + +

    介绍

    打造一款更轻便的消息渲染器,让客户仅需要添加一段script代码即可为自己的网站提供智能在线客服的能力(手机官网、app、小程序等跳转H5链接即可)。

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 技术栈(无任何UI框架)
    "axios": "0.21.0",
    "qiniu-js": "3.1.2",
    "socket.io-client": "2.3.0",
    "v3-carousel": "1.1.1", // 自己封装并发布npm的开源轮播组件
    "vue": "3.0.4",
    "vue-i18n": "9.0.0-beta.15",
    "vuex": "4.0.0-rc.2",
    "xss": "1.0.8",
    "crypto-js": "4.0.0",
    "stylus": "0.54.7",
    "typescript": "4.1.3",
    "vite": "2.3.4",
    + +

    功能

      +
    • 自动邀请框:首次自动弹出、关闭后n秒重新弹出、配置可弹出时机、快捷咨询发起会话、自定义样式、缩小后未读消息提示等
    • +
    • 机器人流程:联想输入、快捷回复按钮、有无帮助、关键词及按钮转人工等
    • +
    • 人工咨询流程:文本消息、富文本消息、emoji表情、文件图片视频、满意度评价(主动、被动、结束会话)、粘贴图片即发送、发送loading与失败重发、已读未读、发送消息节流、会话转接等
    • +
    • 留言流程:表单留言、文本消息留言等
    • +
    • 自定义主题色、自定义弹窗及咨询按钮位置、会话流程日志打印等
    • +
    • 多处轮播广告栏、首次广告语推送、自定义图片视频、iframe嵌入等
    • +
    • 刷新页面记忆流程节点、socket心跳检测及意外断开重连、消息时序问题、多端登录互踢等
    • +
    +

    亮点

      +
    • 多入口打包,同时兼容PCH5的方式打开
    • +
    • 更完善的流程与状态管理,可拖拽的配置方式,客户可自由搭配
    • +
    • 智能引导菜单、机器人、人工、留言、广告推送等多种留资方式
    • +
    • 多种自定义样式模板:pc模板、h5模板、广告模板、小程序模板
    • +
    • 配置信息、聊天内容、socket推送等敏感信息用crypto-js加密
    • +
    +

    难点

      +
    • 首次从〇开始初始化 vue3 + ts 项目
    • +
    • 项目多入口打包方式的构思与实现
    • +
    • socket 的事件推送与处理维护机制
    • +
    • emoji 表情采用雪碧图(精灵图)的形式展示
    • +
    • 消息列表渲染、消息类型区分、列表滚动机制
    • +
    • 移动端兼容问题(键盘遮挡、hover样式不消失等)
    • +
    • 客户页与iframe内部通过postMessage事件通信
    • +
    • 前端用sendStatus字段维护消息的状态
    • +
    • 使用xssFilterXSS方法进行消息过滤
    • +
    • 把富文本消息要绑定的函数挂载到window上即可调用
    • +
    • IOS上点击输入框会被弹起的键盘遮挡住
    • +
    • IOS上不支持直接使用transparent透明属性
    • +
    • Android微信浏览器,点击type=fileinput框选取文件会报错
    • +
    • 手机端上元素点击后:hover样式不会自动消失
    • +
    • 图片加载小图、预加载骨架
    • +
    +
    +

    V7联络云 - 访客端(微信小程序插件)

    1
    2
    3
    4
    5
    6
    7
    8
    gantt

    title 进度周期(甘特图)
    dateFormat YYYY-MM-DD

    section 业务逻辑
    需求评审 :a1, 2021-05-20, 12d
    V1.0 :a2, 2021-06-01, 30d
    + +

    介绍

    将访客端以插件的形式移至微信小程序内,宿主(即插件调用者)仅需使用插件版本号与插件开发者的AppId即可实现智能客服能力。

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 技术栈:
    "@vant/weapp": "1.0.0",
    "crypto-js": "4.0.0",
    "miniprogram-api-promise": "1.0.4",
    "miniprogram-computed": "4.0.4",
    "mobx-miniprogram": "4.13.2",
    "mobx-miniprogram-bindings": "1.2.1",
    "mp-html": "2.1.3",
    "weapp.socket.io": "2.1.0"
    + +

    功能

      +
    • 大部分功能同h5访客端
    • +
    • 座席端的样式配置、基座小程序的功能配置
    • +
    • Emm…
    • +
    +

    亮点

      +
    • Emm…
    • +
    +

    难点

      +
    • uni-app ==> mp-vue ==> wepy ==> taro/cli ==> 原生 + npm
    • +
    • 宿主与插件的事件交互机制问题(宿主直接调用插件函数,传参数或回调函数)
    • +
    • 更新mobx中的引用类型数据后,无法触发视图更新(list=list,obj=Object.assign(obj, obj1))
    • +
    • 在js文件中的非Component模块中以及mobx中,无法获取dom节点(使用scroll-view的scroll-top属性代替手动滚动行为,定义一个超大数值COUNT++)
    • +
    • 不能出现文件循环引用的问题(缺点即:无法将mobx模块化)
    • +
    • socket没有自带的心跳检测(需要配合计时器手动监听)
    • +
    • 没有style标签的概念,无法创建全局样式(只能通过mobx进行数据绑定)
    • +
    • 微信开发者工具的BUG:wxml没有a标签、wxss不能穿透、在插件内调用宿主的路由跳转api报错、text标签内不能换行、插件内不能使用webview_App({})_getApp()方法
    • +
    +
    +

    V7联络云

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    gantt

    title 进度周期(甘特图)
    dateFormat YYYY-MM-DD

    section 业务逻辑
    需求评审 :a1, 2020-11-01, 50d
    V1.0 :a2, 2020-12-22, 66d
    V2.0 :a3, 2021-03-01, 45d
    V3.0 :a4, 2021-04-18, 70d
    + +

    介绍

    采用蚂蚁金融科技提供的 qiankun 微前端框架,将我司现有的 零代码平台 产品 Mopower 嵌入到新开发的 V7联络云 中,实现客户与工单模块的表单配置、用户角色的权限配置、以及客户的关联与应用等功能。

    +

    功能

      +
    • 通话模块和在线客服模块,以及相对应的数据报表中心
    • +
    • 客户模块和工单模块,以及相对应的表单配置模块
    • +
    • 消息中心模块,全局的接入、转接、下载、导入导出的提示
    • +
    • 知识库模块,为在线客服提供更多智能话术模板
    • +
    • OEM自定义域名配置及相关私密信息脱敏
    • +
    +

    亮点

      +
    • qiankun框架的应用解决了iframe的跳转与数据交互的痛点
    • +
    • 使用qiankun微前端框架嵌入其他子应用,并将自身作为子应用嵌入其他基座
    • +
    • 封装vue-router,根据接口数据动态生成路由,且可使用多个字段进行权限控制
    • +
    +

    难点

      +
    • 基座与子应用之间的样式污染问题,特别是子应用插入到基座Body中的Dom元素(采用BEM命名规范的思想与postcss命名空间插件共同解决)。在不开沙箱的情况下使用postcss的插件解决css样式污染和iconfont冲突问题
    • +
    • 使用 bus + keep-alive + vue生命周期 封装qiankun的子应用组件
    • +
    +
    +

    七陌官网重构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    gantt

    title 进度周期(甘特图)
    dateFormat YYYY-MM-DD

    section 业务逻辑
    需求评审 :a1, 2020-11-09, 38d
    V2.0 :a2, 2020-12-18, 53d
    V2.1 :a3, 2021-02-20, 25d
    V2.2 :a4, 2021-04-10, 33d
    + +

    介绍

    由于容联集团于 2021年02月09日 赴美上市,所以对容联七陌的 PC端官网移动端官网 进行代码重构及新页面的改版。

    +

    功能

      +
    • 使用 vue-router 进行路由改版
    • +
    • 引入 gio 埋点
    • +
    +

    亮点

    +

    难点

      +
    • @import引入的css不走px3rem-loaders,改为src的引入形式
      1
      2
      3
      4
      - <style scoped>
      - @import './indexStyle.css';
      + <style scoped src="../../assets/css/indexStyle.css">
      </style>
      + + +
    • +
    +

    叁。 模拟问答

    负责过什么项目,以及在每个项目中的职责?

    答:

    +
      +
    • 七陌专注于做企业通信领域的SAAS云服务提供商,是一家提供智能客服解决方案的。
    • +
    • 虽然我入职仅一年,但已经独立完整的负责过两个项目:
        +
      • 一个是智能客服的访客端,分别打包成js接入和h5链接的形式;
      • +
      • 另一个项目也是访客端,是把现有的访客端开发成原生小程序插件的形式(也就是SDK的形式);
      • +
      +
    • +
    • 还有客服座席端的客户、工单等模块,采用qiankun微前端框架嵌入了子项目的重要模块;
    • +
    • 还有pc端和wap端七陌官网的重构;
    • +
    +

    遇到过什么困难的问题,以及解决方案?

    答:

    +
      +
    • webpack多入口打包
    • +
    • 访客端ui高度自定义化
    • +
    • qiankun样式污染问题
    • +
    • 消息列表渲染,滚动时机
    • +
    • 利用雪碧图做emoji表情墙
    • +
    • 小程序宿主与插件的事件交互
    • +
    • vue3 + ts 学习成本
    • +
    +

    平时如何学习?如何做到 WLB ?

    答:

    +
      +
    • 白天认真工作,工作才是第一生产力
    • +
    • 晚上总结输出,只有多产出才能记住
    • +
    • 平时多交流沟通,每个月会做1-2次技术分享
    • +
    • 定时更新博客,记录学到的内容并学会总结分享
    • +
    +

    肆。 备注

    写点儿啥呢。。。

    +
    \ No newline at end of file diff --git a/scaffolds/draft.md b/scaffolds/draft.md deleted file mode 100644 index 498e95baf..000000000 --- a/scaffolds/draft.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -tags: ---- diff --git a/scaffolds/page.md b/scaffolds/page.md deleted file mode 100644 index f01ba3cd8..000000000 --- a/scaffolds/page.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -date: {{ date }} ---- diff --git a/scaffolds/post.md b/scaffolds/post.md deleted file mode 100644 index ee98e39f5..000000000 --- a/scaffolds/post.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: {{ title }} -categories: - - Vue -tags: - - Vue -keywords: 'Vue' -description: {{ title }} -cover: https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg.png -date: {{ date }} ---- - -> 本文概要 - -# 第一章 - -## 第一节 - -内容 - -```js -// some code -``` - - -# 作者介绍 -- [个人博客](https://leedebug.github.io/) -- [Github](https://github.com/LeeDebug) -- [掘金](https://juejin.cn/user/2189882894323975/posts) -- [语雀](https://www.yuque.com/LeeDebug) -- [简书](https://www.jianshu.com/u/fc47eb26e53c) -- [开源中国](https://my.oschina.net/LeeDebug) -- [博客园](https://www.cnblogs.com/LeeDebug/) -- 微信二维码: - - - - -
    - WeChat:lcc961150665 -
    - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/2020\345\211\215\347\253\257\351\235\242\350\257\225.md" "b/source/_posts/2020\345\211\215\347\253\257\351\235\242\350\257\225.md" deleted file mode 100644 index 8c2f9d58a..000000000 --- "a/source/_posts/2020\345\211\215\347\253\257\351\235\242\350\257\225.md" +++ /dev/null @@ -1,2123 +0,0 @@ ---- -title: 2020前端面试 -categories: - - 面试 -tags: - - 面试 -keywords: '面试' -description: 面试 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170049.png -date: 2020-08-12 23:54:32 ---- - -> 今日裸辞,在投简历的过程中抽出一周集中复习前端知识,并做了此文以记录知识点。 - -# 目 录 -[TOC] - - - -# 自我介绍 -- 学历 -- 项目经验 -- 技术栈 - - - - -# 项目难点 -- vue-print-nb:在windows系统上,低版本的浏览器调用#print打印会出现空白页,换成windows.print()即可;或者让用户更新浏览器 -- vue-print-nb进行单据打印:不同的系统、不同的浏览器,打印效果是不一样的,比如windows系统下element的table-column的props传入的字符串中`英文单词`超过长度会变成...而不会换行,换成scope就没事了 -- vue-fallcalender:日历组件,api介绍模糊,无法实现指定日期的初始化、无法对传入数据进行排序 -- 自定义el-form-item组件:利用slots与props实现自定义样式 -- 封装SimpleClass:每次页面都要loading、axios、赋值、closeLoading,及处理查询参数 -- 封装多文件上传Modal:限制文件大小、类型、个数,默认覆盖上传 -- 封装基于xlsx的Excel导入导出Modal:支持复杂表头的Excel导出 - - - - -# CSS -- [CSS选择器](http://www.ruanyifeng.com/blog/2009/03/css_selectors.html) -{% hideToggle 查看答案 %} - - `*`:通用元素选择器,匹配任何元素 - - `E`:标签选择器,匹配所有使用E标签的元素 - - `info`:class选择器,匹配所有class属性中包含info的元素 - - `#footer`:id选择器,匹配所有id属性等于footer的元素 - - `E,F`:多元素选择器,同时匹配所有E元素或F元素,E和F之间用逗号分隔 - - `E F`:后代元素选择器,匹配所有属于E元素后代的F元素,E和F之间用空格分隔 - - `E > F`:子元素选择器,匹配所有E元素的子元素F - - `E + F`:毗邻元素选择器,匹配所有紧随E元素之后的同级元素F - - `E[att=val]`:匹配所有att属性等于"val"的E元素 - - `E:first-child`:匹配父元素的第一个子元素 - - `E:last-child`:匹配父元素的最后一个子元素,等同于:nth-last-child(1) - - `E:nth-child(n)`:匹配其父元素的第n个子元素,第一个编号为1 -{% endhideToggle %} -- [CSS 布局 position 详解](https://blog.csdn.net/jianghao233/article/details/80534835) -{% hideToggle 查看答案 %} - - fixed:固定定位,参照`整个窗口` - - absolute:绝对定位,参照`最近定位父元素` - - relative:相对定位,参照`父级元素的原始点` - - static:默认值,没有定位,出现在正常的文档流中 - - sticky:吸附定位、磁铁定位、粘性定位。常和fixed结合制造吸附效果 - - inherit:集成父元素的定位 -{% endhideToggle %} -- [css盒模型两种以及切换方式](https://www.axihe.com/focus/css/56.html) -- [实现三列布局](https://blog.csdn.net/u013455430/article/details/86477416) -{% hideToggle 查看答案 %} - - float触发BFC块级布局:float: left;、float: right;、overflow:hidden; - - flex布局:display:flex;、flex:none;、flex:1;、flex:none; - - table布局:display:table;、display:table-cell; - - css计算宽度布局:float:left;、width:calc(100% - 100px);、float:right; -{% endhideToggle %} -- [Flex 布局教程:语法篇](http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html) -- [Flex 布局示例](http://static.vgee.cn/static/index.html) -{% hideToggle 查看答案 %} - - 容器的属性 - - flex-direction属性:决定主轴的方向(即项目的排列方向)。`.box { flex-direction: row | row-reverse | column | column-reverse; }` - - flex-wrap属性:默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,如果一条轴线排不下,如何换行。`.box{ flex-wrap: nowrap | wrap | wrap-reverse; }` - - flex-flow属性:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。`.box { flex-flow: || ; }` - - justify-content属性:定义了项目在主轴上的对齐方式。`.box { justify-content: flex-start | flex-end | center | space-between | space-around; }` - - align-items属性:定义项目在交叉轴上如何对齐。`.box { align-items: flex-start | flex-end | center | baseline | stretch; }` - - align-content属性:定义了多根轴线(多行)的对齐方式。如果项目只有一根轴线,该属性不起作用。`.box { align-content: flex-start | flex-end | center | space-between | space-around | stretch; }` - - 项目的属性 - - order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0 - - flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大 - - flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小 - - flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小 - - flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。 - - align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。 -{% endhideToggle %} -- [实现两栏布局的几种方式](https://www.jianshu.com/p/267239f8e4b2) -- [页面两栏布局(变式)](https://blog.csdn.net/baidu_36065997/article/details/80279305) -- [浮动元素引起的问题和清除浮动的办法](https://www.axihe.com/focus/css/10.html) -{% hideToggle 查看答案 %} - - 额外标签法:`div style="clear:both;"> -
    - -
    - -``` -```css -.box { - width: 80%; -} - -.scale { - width: 100%; - padding-bottom: 56.25%; - height: 0; - position: relative; -} - -.item { - width: 100%; - height: 100%; - background-color: aquamarine; - position: absolute; -} -``` -{% endhideToggle %} -- [transform有哪些用法](https://www.axihe.com/focus/css/75.html) -{% hideToggle 查看答案 %} - - 旋转:rotate(`transform: rotate(45deg);`) - - 缩放:scale(`transform: scale(0.5, 2);`) - - 移动:translate(`transform: skew(45px, 150px);`) - - 倾斜:skew(`transform: skew(30deg, 30deg);`) - - 基准点:transform-origin(`transform-origin: 10px 10px;`) - - 用法:`transform: rotate(45deg) scale(0.5) skew(30deg, 30deg) translate(100px, 100px);`这四种变形方法顺序可以随意,但不同的顺序导致变形结果不同,原因是变形的顺序是从左到右依次进行 -{% endhideToggle %} -- [CSS哪些属性脱离文档流](https://www.axihe.com/focus/css/1.html) -{% hideToggle 查看答案 %} - - 第一种:定位 - - position:absolute - - position:fixed - - 第二种:浮动 - - float:left - - float:right - - 注:position:sticky,就是`粘性定位`并不会脱离文档流 -{% endhideToggle %} -- [css表达式:expression](https://www.cnblogs.com/yingsmirk/archive/2012/04/09/2438506.html) -{% hideToggle 查看答案 %} - - IE5及其以后版本支持在CSS中使用expression,用来把CSS属性和Javascript脚本关联起来 - - 每两小时刷新一次背景色:background-color:expression((new Date().getHours()%2?"#B8D4FF":"#F08A00")); - - 依照浏览器的大小来安置一个元素的位置:left: expression(document.body.offsetWidth - 180 "px"); -{% endhideToggle %} -- [html5/css3响应式页面开发总结](https://www.cnblogs.com/zhangbs/p/9797850.html) -{% hideToggle 查看答案 %} - - `自适应`是一套模板适应所有终端,但每种设备上看到的版式是一样的,俗称宽度自适应。 - - `响应式`一套模板适应所有终端,但每种设备看到的版式可以是`不一样`的。如:http://segmentfault.com/ - - `设置meta标签` - - - - §width=device-width:宽度等于当前设备的宽度 - - §initial-scale:初始的缩放比例(默认设置为1.0) - - §minimum-scale:允许用户缩放到的最小比例(默认设置为1.0) - - §maximum-scale:允许用户缩放到的最大比例(默认设置为1.0) - - §user-scalable:用户是否可以手动缩放(默认设置为no,因为我们不希望用户放大缩小页面) - - `css3的媒体查询` - - @media (orientation:portrait) and (max-width:460px) {} - - orientation:portrait(指定输出设备中的页面可见区域高度大于或等于宽度) | landscape - - - - `百分比布局` -{% endhideToggle %} -- [对BFC的规范的理解](https://www.axihe.com/focus/css/86.html) -{% hideToggle 查看答案 %} - - W3CCSS2.1规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用) - - BFC,`块级格式化上下文`,一个创建了新的BFC的盒子是`独立布局`的,`盒子里面的子元素的样式不会影响到外面的元素` - - 在同一个BFC中的两个毗邻的块级盒在`垂直方向`(和布局方向有关系)的margin会发生折叠 -{% endhideToggle %} -- [触发BFC的条件](https://www.axihe.com/focus/css/87.html) -{% hideToggle 查看答案 %} - - 浮动元素:float除none以外的值,float、right - - 绝对定位元素:position(fixed、absolute) - - display:inline-blocks、table-cells、table-captions - - overflow除了visible以外的值:hidden、auto、scroll -{% endhideToggle %} -- [BFC的用处](https://www.axihe.com/focus/css/87.html#bfc%E7%9A%84%E7%94%A8%E5%A4%84) -{% hideToggle 查看答案 %} - - 可以阻止边距折叠(margin collapsing) - - 可以包含内部元素的浮动 - - 可以阻止元素被浮动覆盖 -{% endhideToggle %} -- [display有哪些值?说明他们的作用。](https://www.axihe.com/focus/css/16.html) -{% hideToggle 查看答案 %} - - `none`:隐藏,此元素不会被显示 - - `block`:块显示;此元素将显示为块级元素,此元素前后会带有换行符 - - `inline`:内嵌,默认。此元素会被显示为内联元素,元素前后没有换行符 - - `table`:表格显示,此元素会作为块级表格来显示(类似table标签),表格前后带有换行符 - - `inline-block`:元素既具有block元素可以`设置宽高`的特性,同时又具有inline元素默认`不换行`的特性 - - `list-item`:象块类型元素一样显示,并添加样式列表标记 - - `inherit`:规定应该从父元素继承display属性的值 -{% endhideToggle %} -- [display中block、inline和inline-block的区别](https://www.jianshu.com/p/02f9d528397c) -{% hideToggle 查看答案 %} - - `block`: - - 前后都有换行,和前后元素都不在一行 - - 宽度、行高、边距都可以自行设置 - - 宽度缺省是它的容器的100%,除非设置一个宽度 - - div,p,h1,form,ul和li是块元素的例子 - - `inline`: - - 和其他元素都在一行上 - - 高,行高及顶和底边距不可改变 - - 宽度就是它的文字或图片的宽度,不可改变 - - span,a,label,input,img,strong和em是inline元素的例子 - - `inline-block`: - - 将对象呈递为内联对象,但是对象的内容作为块对象呈递。旁边的内联对象会被呈递在同一行内,允许空格。(应用此特性的元素呈现为内联对象,周围元素保持在同一行,但可以设置宽度和高度地块元素的属性) -{% endhideToggle %} -- [display:none和visibility:hidden的区别](https://www.axihe.com/focus/css/18.html) -{% hideToggle 查看答案 %} -特性\标签 | display:none | visibility:hidden ----|---|--- -空间占据 | 不占据任何空间,它各边的元素会合拢 | 元素空间依旧存在 -回流与渲染 | 会产生reflow和repaint(回流与重绘) | 没有这个影响前端性能的问题 -株连性 | 其子孙节点元素全部不可见 | 子元素默认不显示,但如果子孙元素应用了visibility:visible就可以展现出来 -{% endhideToggle %} -- [css隐藏页面元素的多种方法](https://www.cnblogs.com/a-cat/p/9039962.html) -{% hideToggle 查看答案 %} -- 常见的CSS预处理器: - - Sass - - Less - - Stylus - - PostCSS:模块化工具,速度快3-30倍,功能强大(既不是`预处理器`也不是`后处理器`) -{% endhideToggle %} -- [sass常用属性](https://www.cnblogs.com/morong/p/4329957.html) -- [less简介](https://www.jianshu.com/p/868d1bcbe12a) -- [谈谈PostCSS](https://segmentfault.com/a/1190000011595620) -- [纯CSS画的基本图形(圆形、三角形、多边形、爱心、八卦等)](https://www.cnblogs.com/zuobaiquan01/p/8582298.html) -- [纯CSS绘制三角形(各种角度)](https://www.jb51.net/article/42513.htm) - - - - -# JavaScript -1. **作用域** -{% hideToggle 查看答案 %} - - 作用域是一种规则,用于确定引擎在何处以及如何根据标识符名称进行变量查找(引擎、编译器、作用域) - - 在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量或抵达最外层作用域(即全局作用域)为止。查找也分为两种:LHS和RHS -{% endhideToggle %} -1. **作用域的两种工作模型** -{% hideToggle 查看答案 %} - - ==词法作用域==:在`写代码或定义时`确定的,作用域链基于作用域的嵌套,即更关注`在何处声明`。大多数编程语言都采用 - - `动态作用域`:在`运行时`确定的,作用域链是基于调用栈的,即更关注函数是`从何处调用`的。this的机制也是如此(像但不是) -{% endhideToggle %} -2. **要判断一个运行中函数的`this绑定`,就需要找到这个函数的`直接调用位置`,最重要的就是`分析调用栈`(为了到达当前执行位置所调用的所有函数,即函数调用链)。** -3. **调用位置如何决定this的绑定对象?可顺序应用下面四条规则来判断** -{% hideToggle 查看答案 %} - - 是否在new中调用(new绑定)?绑定到新创建的对象。`var bar = new foo()` - - 是否通过call、apply(显示绑定)或者bind(硬绑定)调用?绑定到指定的对象。`var bar = foo.call(obj2)` - - 是否在某个上下文对象(隐式绑定)中调用?绑定到那个上下文对象。`var bar = obj1.foo()` - - 如果都不是即独立函数调用(默认绑定),严格模式下绑定到undefined,非严格模式下绑定到全局对象。`var bar = foo()` -{% endhideToggle %} -4. **ES6中`箭头函数()=>{}`并不会使用这四条标准的绑定规则,而是根据`当前的词法作用域`来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。这其实和ES6之前的`self = this`机制一样。** -5. **代码风格:** -{% hideToggle 查看答案 %} - - 词法作用域风格:只使用`词法作用域`,并完全抛弃错误的this风格。 - - this风格:完全采用this风格,必要时使用`bind(...)`,尽量避免使用`self = this`和`箭头函数()=>{}`。 -{% endhideToggle %} -6. **==原型==** -{% hideToggle 查看答案 %} - - 所有的函数都有一个特殊的属性:`prototype(原型)`,prototype属性是一个`指针`,指向的是一个`对象(原型对象)`,原型对象中的`方法和属性`都可以被函数的实例所`共享`。所谓的函数实例是指以函数作为构造函数创建的对象,这些对象实例都可以共享构造函数的原型的方法。(所有`函数`的默认原型都是`Object`的实例) -{% endhideToggle %} -7. **==[作用域链和原型链](https://cnodejs.org/topic/59662f6dbda29e0f7480235b)==** -{% hideToggle 查看答案 %} - - 作用域链:在`词法作用域`中寻找`标识符&变量`。作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。 - - 原型链:在`原型对象`中寻找`引用类型的属性`。 -{% endhideToggle %} -8. **==闭包==** -9. **闭包是基于词法作用域书写代码时所产生的自然结果** -10. **当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使==函数是在当前词法作用域之外执行==** -11. **无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包** -12. **闭包的应用场景:** -{% hideToggle 查看答案 %} - - function调用`setTimeout(...)`(延迟函数的回调会在循环结束时才执行,即使定时器是`setTimeout(..., 0)`) - - 定时器、事件监听器、Ajax请求、跨窗口通信、WebWorkers、任何异步或同步任务,只要使用了==回调函数==,实际上就是在使用闭包 - - for循环 - - 立即执行函数表达式IIFE:`(function(){...})())`(本身创建了闭包,但严格来说并不是闭包) - - ==模块模式==:比如jQuery和$符就是jQuery模块的公共api -{% endhideToggle %} -13. **以上均来自`《你不知道的JavaScript(上卷)》`** -14. **[js的原型和原型链](https://www.jianshu.com/p/be7c95714586)** -{% hideToggle 查看答案 %} - - __proto__指向其构造函数的prototype原型,即 - - `person1.__proto__`,指向`Person.prototype`(相等) - - `Person.prototype`,指向`Object.prototype` - - `Object.prototype`,指向`Null` - - 上述即原型链 -{% endhideToggle %} -15. **[一篇文章看懂_proto_和prototype的关系及区别](https://www.jianshu.com/p/7d58f8f45557)** -16. **[prototype、__proto__与constructor区别与联系](https://blog.csdn.net/qq_35376561/article/details/96651353)** -{% hideToggle 查看答案 %} - - function Person() {} - - Person.prototype.name = 'Lee' - - `Person.prototype` // {% hideInline {name: "Lee", constructor: ƒ}, 查看输出 %} - - Person.constructor // {% hideInline [Function: Function] || ƒ Function() { [native code] }, 查看输出 %} - - Person.__proto__ // {% hideInline {name: "Lee", constructor: ƒ}, 查看输出 %}[Function] || ƒ () { [native code] } - - ---我是一条分割线--- - - var person1 = new Person() - - `person1.prototype` // {% hideInline undefined, 查看输出 %} - - `person1.constructor` // {% hideInline [Function: Person] || ƒ Person() {}, 查看输出 %} - - `person1.__proto__` // {% hideInline {name: "Lee", constructor: ƒ}, 查看输出 %} - - `person1.constructor.prototype` // {% hideInline {name: "Lee", constructor: ƒ}, 查看输出 %} - - person1.constructor.prototype === person1.__proto__ // {% hideInline true, 查看输出 %} -{% endhideToggle %} -1. **[浅析JavaScript中原型及constructor、__proto__、prototype的关系](https://segmentfault.com/a/1190000019279667)** -{% hideToggle 查看答案 %} - - 任意对象:拥有constructor,指向构造函数 - - 任意对象:拥有__proto__,指向构造函数的原型 - - 任意函数:拥有prototype,指向原型对象 - - 任意函数:拥有__proto__和constructor,因为函数也是一种对象,所以这是从它的原型即Object构造函数那里共享来的 -{% endhideToggle %} -1. **[浅拷贝和深拷贝的区别](https://segmentfault.com/a/1190000018874254)** -{% hideToggle 查看答案 %} - - `浅拷贝`是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是`内存地址(引用类型)`,拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。 - - `深拷贝`会另外创造一个一模一样的对象,新对象跟原对象`不共享内存`,修改新对象不会改到原对象。 -{% endhideToggle %} -1. **深拷贝的思路(递归?利用Object.prototype.toString.call(obj)判断类型,链接同上)** -{% hideToggle 查看答案 %} - - 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝 -{% endhideToggle %} -1. **[实现深拷贝的方法](http://www.javanx.cn/20191217/js-deep-copy/)** -{% hideToggle 查看答案 %} - - [JSON.parse(JSON.stringfy(obj))](https://blog.csdn.net/u013565133/article/details/102819929) - - 缺点1:拷贝Date()类型会被转换为字符串 - - 缺点2:RegExp、Error对象,结果将只得到空对象{} - - 缺点3:function、undefined,结果会丢失 - - 缺点4:NaN、Infinity和-Infinity,结果会变成null - - 缺点5:JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的,则使用JSON.parse(JSON.stringify(obj))深拷贝后,结果会`丢弃对象的constructor` - - 递归遍历逐级浅拷贝 - - 每一次递归,无论此函数是否有改变都需要重新递归 - - [proxy深拷贝](https://segmentfault.com/a/1190000021692975) - - 判断target类型,isPlainObject() - - const isProxy = value => !!value && !!value[MY_IMMER] - - 利用上述语句判断是否是我们的proxy类型与对暗号,如果不是proxy,我们需要将其拦截 - - 判断我们深拷贝的对象是否有改变 -{% endhideToggle %} -1. **[函数声明和var声明的优先级](https://www.cnblogs.com/cnblogs-jcy/p/8926310.html)** -2. **[如何利用正则匹配字符串](https://www.jb51.net/article/124041.htm)** -{% hideToggle 查看答案 %} - - exec:查找并返回当前的匹配结果,并以数组的形式返回;exec是RegExp对象的方法;每次只返回一次匹配项 - - match:math是String对象的方法;一次性返回所有匹配项 -{% endhideToggle %} -1. **[将字符串变成数字](https://www.cnblogs.com/mica/p/11760397.html)** -{% hideToggle 查看答案 %} - - 一、当字符串中是纯数字,var s = '234' - - `s *= 1`,字符串在运算操作中会被当做数字类型来处理 - - string的两个转换函数,只对string有效。`parseInt(s)`和`parseFloat(s)` - - `Number(s)`,强制类型转换 - - 二、当字符串是数字加字母等非数字,如var s = '234string',只能用第二种方法`parseInt`、`parseFloat` -{% endhideToggle %} -1. **~~new Set的数组去重和自己实现的哪个性能会更好~~** -2. **[new操作符的作用及运行过程](https://blog.csdn.net/duanshilong/article/details/88235546)(new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例)** -{% hideToggle 查看答案 %} - - 一个继承自 Person.prototype 的新对象被创建。 - - 使用指定的参数调用构造函数 Person ,并将 this 绑定到新创建的对象。new Person 等同于 new Person(),也就是没有指定参数列表,Person 不带任何参数调用的情况。 - - 由构造函数返回的对象就是new表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤) - - `简单回答`: - - 创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。 - - 属性和方法被加入到this引用的对象中。 - - 新创建的对象由this所引用,并且最后隐式的返回this。 -{% endhideToggle %} -1. **[Instanceof运算符的作用及运行过程](https://www.jianshu.com/p/5f6b4ec34059)(用于测试构造函数的prototype属性,是否出现在对象的原型链中的任何位置)** -{% hideToggle 查看答案 %} - - 判断构造函数的原型对象(如Person.prototype和Object.prototype)是否在实例对象(person1)的原型链上(__proto__);如果在对象的原型链上,就返回true,如果不在就返回false; -{% endhideToggle %} -1. **变量提升是什么?Var、Const 、let 是如何变量提升的(暂时性死区)?** -2. **[var与let、const的区别](https://www.jianshu.com/p/7bf685dbc12e)** -{% hideToggle 查看答案 %} - - var声明变量存在变量提升,let和const不存在变量提升 - - let、const都是块级局部变量 - - 同一作用域下let和const不能声明同名变量,而var可以 -{% endhideToggle %} -1. **[js检测变量是否已定义](https://blog.csdn.net/joyvonlee/article/details/89603278)** -{% hideToggle 查看答案 %} - - console.log(typeof undefined); // undefined - - console.log(typeof null); // object - - console.log(typeof 123); // number - - console.log(typeof "字符串"); // string - - console.log(typeof {}); // object - - console.log(typeof aa); // undefined -{% endhideToggle %} -1. **[js判断对象中是否有某属性的常用方法](https://www.jb51.net/article/141994.htm)** -{% hideToggle 查看答案 %} - - 点(`.x`)或者方括号(`[x]`):这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。局限性:不能用在x的属性值存在,但可能为undefined的场景 - - `in运算符`:如果指定的属性在指定的对象或其原型链中,则in运算符返回true。局限性:无法区分自身和原型链上的属性,即无法判断该属性是否是自身的 - - `hasOwnProperty()`:适用于只判断自身属性的场景。 -{% endhideToggle %} -1. **[ES6用过的新特性](https://juejin.im/post/6844903991550181390?utm_source=gold_browser_extension)** -{% hideToggle 查看答案 %} - - 箭头函数、字符串插值、const、let(块作用域) - - Promises异步机制、模块import导入、模块export导出 - - function默认参数、class类定义与extend继承 - - for-of遍历对象、...展开操作符 -{% endhideToggle %} -1. **[es6中的数组方法](https://es6.ruanyifeng.com/#docs/array)** -{% hideToggle 查看答案 %} - - 扩展运算符(...) - - Array.from() - - Array.of() - - 数组实例的 copyWithin() - - 数组实例的 find() 和 findIndex() - - 数组实例的 fill() - - 数组实例的 entries(),keys() 和 values() - - 数组实例的 includes() - - 数组实例的 flat(),flatMap() - - 数组的空位 - - Array.prototype.sort()的排序稳定性 -{% endhideToggle %} -1. **- [es6中的对象方法](https://es6.ruanyifeng.com/#docs/object)** -{% hideToggle 查看答案 %} - - 属性的简洁表示法:module.exports = { getItem, setItem, clear }; - - 属性名表达式:obj['a' + 'bc'] = 123; - - 方法的name属性 - - 属性的可枚举性和遍历 - - 有四个操作会忽略enumerable为false的属性 - - for...in循环:只遍历对象自身的和继承的可枚举的属性。 - - Object.keys():返回对象自身的所有可枚举的属性的键名。 - - JSON.stringify():只串行化对象自身的可枚举的属性。 - - Object.assign():忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。 - - super关键字:指向当前对象的原型对象。 - - `super.foo`等同于`Object.getPrototypeOf(this).foo(属性)`或`Object.getPrototypeOf(this).foo.call(this)(方法)` - - 对象的扩展运算符:let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; - - 链判断运算符 - - obj?.prop // 对象属性 - - obj?.[expr] // 同上 - - func?.(...args) // 函数或对象方法的调用 - - Null 判断运算符 - - const animationDuration = response.settings?.animationDuration ?? 300; -{% endhideToggle %} -1. **- [JS继承的实现方式](https://www.cnblogs.com/humin/p/4556820.html)** -{% hideToggle 查看答案 %} - - 原型链继承:将父类的实例作为子类的原型。`function Cat(){}; Cat.prototype = new Animal()` - - 构造继承:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)。`function Cat(name){Animal.call(this);this.name = name || 'Tom';}` - - 实例集成:为父类实例添加新特性,作为子类实例返回。`function Cat(name){var instance = new Animal();instance.name = name || 'Tom';return instance;}` - - 拷贝继承 - - 组合继承 - - 寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点 -{% endhideToggle %} -1. **- [es6中的对象新增方法](https://es6.ruanyifeng.com/#docs/object-methods)** -{% hideToggle 查看答案 %} - - Object.is():`Object.is('foo', 'foo')`、`Object.is({}, {})` - - ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。 - - Object.assign(target, source1, source2) - - 用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target) - - Object.getOwnPropertyDescriptors(obj) - - 返回指定对象所有自身属性(非继承属性)的描述对象(descriptor) - - `{foo:{configurable:true,enumerable:true,value:123,writable:true}}` - - __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf() - - `Object.getPrototypeOf(person1)===person1.constructor.prototype` - - Object.keys(),Object.values(),Object.entries() - - Object.fromEntries() - - 是Object.entries()的逆操作,用于将一个键值对数组转为对象 -{% endhideToggle %} -1. **- [js es6遍历对象的6种方法(应用中推荐前三种)](https://www.cnblogs.com/yuer20180726/p/11377897.html)** -{% hideToggle 查看答案 %} - - `Object.keys()、Object.values()、Object.entries()`:返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性) - - `for…in…`:循环遍历对象自身的和继承的可枚举属性(不含Symbol属性) - - `Object.getOwnPropertyNames(obj)`:返回一个数组,包含对象自身的所有属性(包括`不可枚举属性`,但不含Symbol属性) - - `Reflect.ownKeys(obj)`:返回一个数组,包含对象自身的`所有属性`(不管属性名是Symbol或字符串,也不管是否可枚举) -{% endhideToggle %} -1. **- [for…in…遍历对象时,为什么有的对象属性可以被遍历到,有的不行?](https://www.jianshu.com/p/c6e3f4685963)** -{% hideToggle 查看答案 %} - - js中基本包装类型的`自带原型属性`是不可枚举的,如Object, Array, Number等 - - Object.defineProperty()定义的`enumerable:false`的属性也是不可枚举的 -{% endhideToggle %} -33. **- [for-in和for-of的区别](https://www.jianshu.com/p/325f05c95c6a)** -{% hideToggle 查看答案 %} - - for...in循环出的是key值,for...of循环出的是value值 - - 推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of - - for...of不能循环普通的对象,需要通过和Object.keys()搭配使用 -{% endhideToggle %} -34. **- [for-of的工作过程](https://www.jianshu.com/p/c43f418d6bf0)** -{% hideToggle 查看答案 %} - - 首先调用集合的`Symbol.iterator`方法,返回一个新的`迭代器对象`(迭代器对象可以是任意具有.next()方法的对象) - - for-of循环将重复调用这个`.next()`方法,每次循环调用一次 -{% endhideToggle %} -35. **- a.b.c()中c的this(idea自己实现)** -{% hideToggle 查看答案 %} -```js -var x = '我是a' -var a = { - x: '我是a', - b: { - x: '我是b', - c: function () { - var x = '我是c' - console.log(this) - } - } -} -a.b.c() // { x: '我是b', c: [Function: c] } -var d = { - x: '我是d', - e: () => a.b.c() -} -d.e() // { x: '我是b', c: [Function: c] } - -// 如果把c改为箭头函数 -c: () => { - var x = '我是c' - console.log(this) -} -// 输出结果如下 -a.b.c() // Window {...} -d.e() // Window {...} -``` -{% endhideToggle %} -1. **[forEach和for/for...of循环在使用await上的区别](https://blog.csdn.net/u012961419/article/details/107490076),如果想同时执行呢?(promise.all)** -{% hideToggle 查看答案 %} - - for/for...of:遍历到每个元素后,执行await后的方法 - - forEach:在遍历每个元素后,执行的是该方法接收的`回调函数`方法,然后在回调中,执行await方法,forEach方法内部调用回调函数时,并没有使用await修饰,所以回调方法并不会等待上一个回调执行完毕。所以,内部的await也就失去了意义。 -{% endhideToggle %} -1. **[IIEF立即执行为什么用两对括号包裹](https://www.jb51.net/article/127527.htm)** -{% hideToggle 查看答案 %} - - 模拟函数执行 - - (function () { return 233 })() // 233 - - (function () { return 233 }()) // 233 - - +function () { return 233 }() // 233 - - -function () { return 233 }() // -233 - - ~function () { return 233 }() // -233 - - !function () { return 233 }() // false -{% endhideToggle %} -1. **[`[]+{}`和`{}+[]`结果有何不同](https://blog.csdn.net/carcarrot/article/details/97786733)** -{% hideToggle 查看答案 %} - - 加法会进行隐式类型转换,规则是调用其valueOf()或toString()以取得一个非对象的值(primitive value) - - []+{} 结果是 "[object Object]",字符串串接 - - {}+[] 结果是 0,{}解析为{ // empty block },即对一个空数组执行正号运算,实际上就是把数组转型为数字 - - ({}+[]) 结果是 "[object Object]" - - +[] 结果是 0 ,此处+为正号 - - +{} 结果是 NaN ,此处+为正号 - - [].toString() 结果是 "" - - ({}).toString() 结果是 "[object Object]" - - {}.toString():会出错,Uncaught SyntaxError: Unexpected token . - - 0+[] 结果是 "0" - - 0+{} 结果是 "0[object Object]" -{% endhideToggle %} -1. **[var a=b=c=d=5是什么意思?如果接下来再写一句,d=9,a,b,c的值会变化吗?](https://www.axihe.com/focus/js/base/13.html)** -{% hideToggle 查看答案 %} - - 初始化给a、b、c、d赋值都为5 - - `不改变`,改变d后a、b、c值不会改变,因为a、b、c、d都是值类型的变量,各自的值存在于自己的栈当中,当d变化了其他栈中的值不改变。 -{% endhideToggle %} -1. **[var a=b=c=d=【1,2,3,4,5】是什么意思?如果接下来写一句d【5】=9;a,b,c,的值会发生变化吗](https://www.axihe.com/focus/js/base/14.html)** -{% hideToggle 查看答案 %} - - `发生变化`,a、b、c、d值都改为[1,2,3,4,5,9] - - 因为a、b、c、d是引用类型,引用类型的数据存在于堆当中,栈中存的是指向堆的地址,初始化时 a、b、c、d在各自的栈中指向的堆是同一个,该堆保存着[1,2,3,4,5],当改变了堆中的值,其他对象跟着改变。 -{% endhideToggle %} -1. **[var a=b=c=d=【1,2,3,4,5】是什么意思?如果接下来再写一句d=【9】;a,b,c的值会发生变化吗](https://www.axihe.com/focus/js/base/15.html)** -{% hideToggle 查看答案 %} - - `不改变`,因为对于d来说改变的是d栈中的地址,此时d指向的堆已经不是原地址,所以此时d与其他几个对象的值已经不同了。 - - 考点:对比上一题,改变的是对应堆中的值,而此题是将d栈中的指针地址改变了。 -{% endhideToggle %} -1. **[js表达式和语句的区别](https://zhidao.baidu.com/question/1758450597152165508.html)** -{% hideToggle 查看答案 %} - - `表达式`是由运算符构成,并运算产生结果的语法结构。每个表达式都会产生一个值,它可以放在任何需要一个值的地方,比如作为一个函数调用的参数 - - `语句`:则是由“;(分号)”分隔的句子或命令。如果在表达式后面加上一个“;”分隔符,这就被称为“表达式语句”。它表明“只有表达式,而没有其他语法元素的语句” - - 声明语句:变量声明和函数声明 - - 赋值语句 - - 控制语句:能够对语句执行顺序产生改变,包括条件语句和循环语句,当然还有比较特殊的标签语句 - - 表达式语句:这些语句去掉最后分号,都也可当表达式用的。常见的有:对象操作(new、delete)、函数调用(函数执行,必有返回值)等 -{% endhideToggle %} -1. **~~async 和 await 是谁的语法糖~~** -2. **~~generator 又是如何实现的?~~** -3. **[js的5种基本和1(2)种引用数据类型](https://www.axihe.com/focus/js/base/12.html)** -{% hideToggle 查看答案 %} - - 基本数据类型 :String、Number、Boolean 、Null、Undefined、Symbol、BigInt - - 引用数据类型:Object(Funtion) - - 判断方法:Object.prototype.toString.call(data) -{% endhideToggle %} -1. **null与undefined知识点(链接同上)** -{% hideToggle 查看答案 %} - - undefined值派生自null,所以相等比较`null==undefined为true` - - null是空指针但未用,有个占位符,undefined连指针都没有,也没有占位符,所以绝对比较`null===undefined为false` -{% endhideToggle %} -1. **[堆和栈的区别](https://blog.csdn.net/hupech/article/details/90522120)(第二篇帖子)** -{% hideToggle 查看答案 %} - - 栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的`栈`。 - - 堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于`链表`,呵呵。 -{% endhideToggle %} -13. **[为什么会有跨域的问题?](https://www.jianshu.com/p/9b6b6a135432)** -{% hideToggle 查看答案 %} - - 在`前后端分离`的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。在请求的过程中我们要想回去数据一般都是post/get请求,所以跨域问题出现 - - 跨域问题来源于JavaScript的`同源策略`,即只有`协议+主机名(域名)+端口号(如存在)`相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。 - - 跨域问题是针对`JS和ajax`的,`html本身没有跨域问题`(比如a标签、script标签、甚至form标签,只要是带src的)可以直接跨域发送数据并接收数据 -{% endhideToggle %} -1. **[九种跨域方式实现原理](https://zhuanlan.zhihu.com/p/56718905)** -{% hideToggle 查看答案 %} - - `开发环境`:webpack自带proxyTable:`target:'http://192.168.0.123:8080/'、pathRewrite:{'^/api': '/api'}` - - `生产环境`:利用nginx反向代理,将请求分发到部署到相应项目的服务器上:`location ~ /api { proxy_pass http://127.0.0.1:23480; }` - - JSONP:ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加。(但JSONP只支持`GET请求`,不支持POST请求)[jsonp原理详解](https://blog.csdn.net/hansexploration/article/details/80314948) - - CORS:是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),CORS通信与同源的AJAX通信没有差别,就是在请求头信息中增加了`Origin:http://192.168.0.123:8080/`字段。[详见阮一峰的博客](http://www.ruanyifeng.com/blog/2016/04/cors.html) - - web sockets:在JS创建了webSocket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为`web sockt协议`。只有在支持webSocket协议的服务器上才能正常工作。(http->ws; https->wss) -{% endhideToggle %} -1. **[JavaScript手动实现JSONP代码](https://www.cnblogs.com/gogolee/p/6439664.html)** -{% hideToggle 查看答案 %} - - document.createElement('script') - - jsonpScript.setAttribute('src', `${url}callback=${callbackFunction}`) - - (完成后)document.getElementsByTagName('head')[0].removeChild(script) -{% endhideToggle %} -1. **[对前端模块化的认识](https://blog.csdn.net/u014168594/article/details/77099315)** -{% hideToggle 查看答案 %} - - CommonJS规范:允许模块通过`require`方法来同步加载(同步意味阻塞)所要依赖的其他模块,然后通过`module.exports`来导出需要暴露的接口。CommonJS是以在浏览器环境之外构建JavaScript生态系统为目标而产生的项目,比如在服务器和桌面环境中。 - - AMD标准:AMD是RequireJS在推广过程中对模块定义的规范化产出(异步模块定义)。 - - AMD标准中定义了以下两个API: - - require([module], callback);用来加载一系列模块 - - define(id, [depends], callback);用来定义并暴露一个模块 - - 优点: - - `异步加载,提前执行` - - 适合在浏览器环境中加载模块,且可以并行加载多个模块 - - CMD标准:CMD是SeaJS在推广过程中对模块定义的规范化产出(在CommomJS和AMD基础上提出)。 - - define(function (requie, exports, module) {}); - - 优点: - - `依赖就近,延迟执行` - - 可以很容易在服务器中运行 - - ES6模块化:EcmaScript6标准增加了JavaScript语言层面的模块体系定义(关键字)。在 ES6 中,我们使用`import`关键字引用模块,使用`export`关键字来导出模块。 -{% endhideToggle %} -1. **[四种常见的JS模块化管理方法的比较(表格形式:导入语法、导出语法、加载方式等)](https://blog.csdn.net/Mrfive555/article/details/79668797?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param)** -2. **AMD 和 CMD 的区别** -{% hideToggle 查看答案 %} - - 对于依赖的模块,AMD是提前执行,CMD是延迟执行。 - - AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require。 - - AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一 -{% endhideToggle %} -1. **ES6 模块与 CommonJS 模块的差异** -{% hideToggle 查看答案 %} - - CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 - - CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。 - - 即 CommonJS 先加载整个模块,输出一个对象,取对象内相应的值,输出后内部不会再变化;ES6 是静态编译命令,先加载一个引用,等执行时再根据引用到加载模块内取值输出,动态引用不缓存。 - - 目前很少JS引擎能直接支持 ES6 标准,因此 Babel 的做法实际上是将不被支持的 import 翻译成目前已被支持的 require。 -{% endhideToggle %} -1. **[import和require的区别](https://www.cnblogs.com/sunshq/p/7922182.html)** -{% hideToggle 查看答案 %} - - 遵循规范 - - require 是 AMD规范引入方式 - - import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法 - - 调用时间 - - require是运行时调用,所以require理论上可以运用在代码的任何地方 - - import是编译时调用,所以必须放在文件开头 - - 本质 - - require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量 - - import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require -{% endhideToggle %} -1. **~~XML和JSON的区别?~~** -2. **[什么是dom0,dom1,dom2,dom3](https://zhidao.baidu.com/question/1771299590043658140.html)** -{% hideToggle 查看答案 %} - - DOM0:不是W3C规范。 - - DOM1:开始是W3C规范。专注于HTML文档和XML文档。 - - DOM2:对DOM1增加了样式表对象模型 - - DOM3:对DOM2增加了内容模型(DTD、Schemas) 和文档验证。 -{% endhideToggle %} -1. **[dom0级和dom2级的区别](https://www.imooc.com/qadetail/168609)** -{% hideToggle 查看答案 %} - - DOM0级事件处理:简单且具有跨浏览器的优势;但函数会被覆盖,只会执行最后一个的函数 - - DOM2级事件处理:不会覆盖且顺序执行;不具有跨浏览器优势 -{% endhideToggle %} -1. **[事件,事件冒泡和事件捕获](https://www.cnblogs.com/Shinigami/p/10054696.html)** -2. **[addEventListener的第三个参数到底该怎么设置](https://www.jianshu.com/p/bad857d649f2)** -{% hideToggle 查看答案 %} - - target.addEventListener(type, listener[, useCapture]); - - useCapture: 默认值为false(即 使用事件冒泡) -{% endhideToggle %} -1. **[JS阻止冒泡和取消默认事件(默认行为)](http://caibaojian.com/javascript-stoppropagation-preventdefault.html)** -{% hideToggle 查看答案 %} - - `@click.stop="func($event)"`vue取消事件冒泡 - - `@click.prevent="func($event)"`vue阻止默认事件 - - `event.stopPropagation()`W3C提供的方法,起到阻止捕获和冒泡阶段中当前事件的进一步传播 - - `event.preventDefault()`可以取消默认事件 - - `e.cancelBubble = true`,只针对IE -{% endhideToggle %} -1. **Javascript垃圾回收方法** -{% hideToggle 查看答案 %} - - 标记清除(mark and sweep)是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了 - - 引用计数(reference counting) 是另一种不太常见的垃圾回收策略。在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。 - - 在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用问题。 -{% endhideToggle %} -1. **[Web Worker](http://www.ruanyifeng.com/blog/2018/07/web-worker.html)** -{% hideToggle 查看答案 %} - - 作用就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker线程在后台运行,两者互不干扰。等到Worker线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被Worker线程负担了,主线程(通常负责UI交互)就会很流畅,不会被阻塞或拖慢。 - - 通过 worker = new Worker(url) 加载一个JS文件来创建一个worker,同时返回一个worker实例。 - - 通过worker.postMessage( data) 方法来向worker发送数据。 - - 绑定worker.onmessage方法来接收worker发送过来的数据。 - - 可以使用 worker.terminate() 来终止一个worker的执行。 -{% endhideToggle %} -1. **[js按位运算符](https://www.cnblogs.com/happy1992/p/7064114.html)** -{% hideToggle 查看答案 %} - - `&` 按位与 - - `|` 按位或 - - `^` 按位异或 - - `~` 按位非 - - `>>` 右移 - - `<<` 左移 -{% endhideToggle %} -1. **[常见的js位运算](https://www.jianshu.com/p/768f50569130)** -{% hideToggle 查看答案 %} -```js -一、取整 -// 异或运算取整::位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。 -// 所以,将一个小数与0进行或运算,等同于对该数去除小数部分,即取整数位。 -12.9 ^ 0 // 12 --12.9 ^ 0 // -12 - -// 双否定位操作符取整 -~~4.9 // 4 -~~(-4.9) // -4 - -// 左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效。 -13.5 << 0 // 13 --13.5 << 0 // -13 - -// 或运算取整:位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。 -// 所以,将一个小数与0进行或运算,等同于对该数去除小数部分,即取整数位。 -2.9 | 0 // 2 --2.9 | 0 // -2 - -二、判断奇数、偶数 -function assert(n) { - if (n & 1) { - console.log("n是奇数"); - } else { - console.log("n是偶数"); - } -} - -& 是按位与运算 -1 & 1 = 1 -1 & 0 = 0 -0 & 0 = 0 -0 & 1 = 0 -int型变量在一般内存中占用4个字节 -∵ 2 = 00000000 00000000 00000000 0000010 -∵ 1 = 00000000 00000000 00000000 0000001 -∴ 2 & 1 = 0 -``` -{% endhideToggle %} -1. **[2..toString](https://blog.csdn.net/newair1798/article/details/88207671)** -2. **[为什么0.1+0.2不等于0.3?](https://segmentfault.com/a/1190000012175422)** -{% hideToggle 查看答案 %} - - 原因在于在JS中采用的IEEE_754的双精度标准,计算机内部存储数据的编码的时候,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1。 - - 两个`有舍入误差的值`在求和时,相互抵消了,但这种“负负得正,相互抵消”不一定是可靠的,当这两个数字是用不同长度数位来表示的浮点数时,舍入误差可能不会相互抵消。 - - 又如,对于0.1+0.3,结果其实并不是0.4,但0.4是最接近真实结果的数,比其它任何浮点数都更接近。许多语言也就直接显示结果为0.4了,而不展示一个浮点数的真实结果了。 -{% endhideToggle %} -1. **[数组的降维5种办法](https://blog.csdn.net/xufeiayang/article/details/90111775)** -{% hideToggle 查看答案 %} - - 数组字符串化:`arr += ''; arr = arr.split(',');` - - 递归,判断后push或concat - - `Array.prototype.flat()` - - 使用reduce、concat和递归无限反嵌套多层嵌套的数组 -{% endhideToggle %} -1. **[数组的join](https://www.w3school.com.cn/jsref/jsref_join.asp):把数组中的所有元素拼接成一个字符串,元素是通过指定的分隔符进行分隔的。** -5. **[字符串的split](https://www.w3school.com.cn/jsref/jsref_split.asp):把一个字符串通过指定的分隔符分割成字符串数组。** -6. **[数组的map和forEach的区别](https://www.jianshu.com/p/83aa9a2a4055)** -{% hideToggle 查看答案 %} - - map()方法:`创建一个新的数组`,其中每一个元素由调用数组中的每一个元素执行提供的函数得来,所以可以使用复合(composition)(map(),filter(),reduce()等组合使用)来玩出更多的花样 - - foreEach()方法:针对每一个元素执行提供的函数,不返回新数组,`返回值为undefined` -{% endhideToggle %} -1. **[js如何判断数据类型](https://www.cnblogs.com/yi0921/p/6183422.html)** -{% hideToggle 查看答案 %} - - `Object.prototype.toString.call(data)`:最全面,但需要注意区分大小写 - - `typeof`操作符:不适合用于判断是否为数组。当使用typeof判断数组和对象的时候,都会返回object。可以使用isArray()来判断是否为数组 - - `instanceof`:只能用来判断对象和函数,不能用来判断字符串和数字等。判断它是否为字符串和数字时,只会返回false - - `constructor`:返回对创建此对象的数组函数的引用 -{% endhideToggle %} -1. **[ES6中Promise.all和Promise.race区别](https://www.cnblogs.com/richard1015/p/9155564.html)** -{% hideToggle 查看答案 %} - - Promise.all:都成功才会调用success,如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。 - - Promise.race:只要有一个成功就会调用success,但是进程不会立即停止 - - ~~对promise的考察,then链的应用~~ -{% endhideToggle %} -1. **[Document.cookie-MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie)** -2. **[js如何操作cookie](https://www.jianshu.com/p/cf557cfe460c)** -{% hideToggle 查看答案 %} - - `set`:document.cookie = key + "=" + val + ";expires=" + date.toGMTString(); - - `get`:document.cookie.replace(/[ ]/g, "").split(";") - - `delete`:document.cookie = key + "=v; expires =" + date.toGMTString(); - - 或 利用小框架:`cookies.js` -{% endhideToggle %} -1. **[Cookie和Session的使用和区别](https://www.jianshu.com/p/9a561b36e9f3)** -2. **[js进程(process)和线程(thread)的区别](https://zhidao.baidu.com/question/1308689490865592819.html)** -{% hideToggle 查看答案 %} - - 进程:操作系统分配的占有CPU资源的最小单位。拥有独立的地址空间。 - - 线程:安排CPU执行的最小单位。同一个进程下的所有线程,共享进程的地址空间。 -{% endhideToggle %} -1. **[js进程(process)和线程(thread)的关系](https://www.cnblogs.com/yu-hailong/p/9596431.html)** -{% hideToggle 查看答案 %} - - 以多进程形式,允许多个任务同时运行 - - 以多线程形式,允许单个任务分成不同的部分运行 - - 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源 - - 以下是详细解释: - - 进程它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态 - - 一个进程可以包括多个线程 - - 一个进程的内存空间是共享的,每个线程都可以使用这些共享内存 - - 一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存 - - 一个防止其他线程使用的简单方法`"互斥锁"`(Mutual exclusion,缩写Mutex),防止多个线程同时读写某一块内存区域 -{% endhideToggle %} -6. **[Object.create实现了什么?传null得到的结果和普通对象有什么区别?](https://www.jianshu.com/p/28d85bebe599)** -{% hideToggle 查看答案 %} - - 创建对象的方式不同 - - newObject():通过构造函数来创建对象, 添加的属性是在自身实例下。`console.log(b)//{rep:"apple"} console.log(b.__proto__)//{}` - - Object.create():创建对象的另一种方式,可以理解为继承一个对象,添加的属性是在原型下。`console.log(b)//{} console.log(b.__proto__)//{rep:"apple"}` - - 创建对象属性的性质不同,Object.getOwnPropertyDescriptors(obj) - - newObject():{value:42,writable:true,enumerable:true,configurable:true} - - Object.create():{value:42,writable:false,enumerable:false,configurable:false} - - 创建空对象时不同 - - newObject():`__proto:Object` - - Object.create():`No properties` - - `__proto__`属性 - - newObject():? - - Object.create():? -{% endhideToggle %} -1. **[call、apply和bind有何区别](https://www.cnblogs.com/cosiray/p/4512969.html)** -{% hideToggle 查看答案 %} - - call对函数直接调用:`xw.say.call(xh,"实验小学","六年级")` - - apply对函数直接调用:`xw.say.apply(xh,["实验小学","六年级"])` - - bind返回的是一个函数,需要手动()调用,且可在调用时传参:`xw.say.bind(xh,"实验小学","六年级")()`或`xw.say.bind(xh)("实验小学","六年级")` -{% endhideToggle %} -8. **[手写实现call、apply、bind](https://www.jianshu.com/p/57a876fe66c8)** -{% hideToggle 查看答案 %} -模拟call -```js -Function.prototype.myCall = function(obj){ - if(obj === null || obj === undefined){ - obj = window; - } else { - obj = Object(obj); - } - let arg = []; - let val ; - for(let i = 1 ; i 需要防抖的函数; - *delaytime --> 毫秒数,防抖所需期限值; -*/ -function debounce(fn,delaytime){ - let timer = null - return function(){ - if(timer){ - clearTimeout(timer) //进入这里说明当前存在一个执行过程,并且同时又执行了一个相同事件,故取消当前的执行过程 - } - timer = setTimeout(fn,delaytime) - } -} -function show_scrollPosition(){ - var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop; - console.log("当前滚动条位置为:",scrollPosition); -} - -window.onscroll = debounce(show_scrollPosition,1000) -``` - -节流(throttle):(第一次)规定一个期限时间,在该时间内,触发事件的回调函数只能执行一次,如果期限时间内回调函数被多次触发,则只有一次能生效。(页面滚动到底部就加载更多) -```js -function throttle(fn, delay) { - let last_time - let timer = null - return function () { - let cur_time = new Date().getTime() - if (last_time && cur_time < last_time + delay) { //若为真,则表示上次执行过,且在期限值范围内 - clearTimeout(timer) - timer = setTimeout(() => { - fn(); - last_time = cur_time - }, delay) - } else { - last_time = cur_time; - fn(); - } - - } -} - -function show_scrollPosition() { - var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop; - console.log("当前滚动条位置为:", scrollPosition); -} - -window.onscroll = throttle(show_scrollPosition, 1000) -``` -{% endhideToggle %} - -1. **实现请求并发限制,具体为:封装一个函数,传递请求并发的个数为参数,实现对并发请求的限制** -2. **说说闭包以及垃圾回收机制** -3. **利用async和await如何处理异常事件** -4. **箭头函数和普通函数有什么区别?如果想改变箭头函数中绑定this怎么办?** -5. **原生js判断鼠标在一个有对角线矩形的位置** - - - - -# Vue 2.x - -## 源码相关 - -1. **[BiliBili: Vue 2.x 源码解读(12) —— path阶段](https://www.bilibili.com/video/BV1rQ4y1o7WF/?spm_id_from=333.788.recommend_more_video.1)** - - Emm...,慢慢看 - -2. **v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?** -{% hideToggle 查看答案 %} - - 源码: `compiler/codegen/index.js` - - 在`Vue 2.x`版本中,`v-if`的优先级大于`v-for` - - 在`Vue 3.x`版本中,`v-for`的优先级大于`v-if` - - `render`函数: `with(this){return _c('div', { ... })}` -{% endhideToggle %} - -1. **Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?** -{% hideToggle 查看答案 %} - - 源码: `src\core\instance\state.js - initData()` - - Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的 - - 采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题 - - 而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况 -{% endhideToggle %} - -1. **你知道vue中key的作用和工作原理吗?说说你对它的理解。** -{% hideToggle 查看答案 %} - - 源码: `src\core\vdom\patch.js - updateChildren()` - - key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两 个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操 作量,提高性能 - - ~~另外,若不设置key还可能在列表更新时引发一些隐蔽的bug(暂时未知)~~ - - vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果 -{% endhideToggle %} - -5. **你怎么理解vue中的diff算法?** -{% hideToggle 查看答案 %} - - 源码1: 必要性,`lifecycle.js - mountComponent()` - - 组件中可能存在很多个data中的key使用 - - 源码2: 执行方式,`patch.js - patchVnode()` - - patchVnode是diff发生的地方,整体策略: 深度优先,同层比较 - - 源码3: 高效性,`patch.js - updateChildren()` - - diff算法是虚拟DOM技术的必然产物: 通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上 - - 另外,也需要 diff 高效的执行对比过程,从而降低时间复杂度为O(n) - - vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方 - - vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch - - diff过程整体遵循`深度优先、同层比较`的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做`首首、尾尾、首尾、尾首`4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。 -{% endhideToggle %} - -6. **谈一谈对vue组件化的理解?** -{% hideToggle 查看答案 %} - - 源码1: 组件定义,`src\core\global-api\assets.js` - - vue-loader会编译template为render函数,最终导出的依然是组件配置对象 - - 源码2: 组件化优点,`lifecycle.js - mountComponent()` - - 组件、Watcher、渲染函数和更新函数之间的关系 - - 源码3: 组件化实现: 构造函数,`src\core\global-api\extend.js`、实例化及挂载,`src\core\vdom\patch.js - createElm()` - - 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用; - - 组件化开发能大幅提高应用开发效率、测试性、复用性等; - - 组件使用按分类有: 页面组件、业务组件、通用组件; - - vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue; - - vue中常见组件化技术有: 属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等; - - 合理的划分组件,有助于提升应用性能; - - 组件应该是高内聚、低耦合的; - - 遵循单向数据流的原则。 -{% endhideToggle %} - -7. **谈一谈对vue设计原则的理解?** -{% hideToggle 查看答案 %} - - 在vue的官网上写着大大的定义和特点: `渐进式JavaScript框架、易用、灵活和高效` -{% endhideToggle %} - -8. **谈谈你对MVC、MVP和MVVM的理解?** - - 源码: `compiler` - -9. **你了解哪些Vue性能优化方法?** -{% hideToggle 查看答案 %} - - 路由懒加载 - - keep-alive缓存页面 - - 使用v-show复用DOM - - v-for 遍历避免同时使用 v-if - - 长列表性能优化,静态列表:`list = Object.freeze([])` - - 虚拟滚动:[vxe-table](https://github.com/x-extends/vxe-table) - - 事件的销毁,Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。`beforeDestroy() { clearInterval(this.timer) }` - - 图片懒加载,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域 内的图片先不做加载,等到滚动到可视区域后再去加载。参考项目:`vue-lazyload`,代码:`` - - 第三方插件按需引入,`import { Button, Select } from 'element-ui';` - - 无状态的组件标记为函数式组件,`` - - 子组件分割,独立可复用功能可抽象出来 - - 变量本地化,如果有for循环等频繁访问`this.xxx`的情况,提前赋值给本地变量 - - SSR -{% endhideToggle %} - -1. **简单说一说vuex使用及其理解?** -{% hideToggle 查看答案 %} - - Vuex实现了一个单向数据流,在全局拥有一个state存放数据,当组件要更改state中的数据时,必须通过mutation提交修改信息,mutation同时提供了订阅者模式供外部插件调用获取state数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作 需要走action,但action也是无法直接修改state的,还是需要通过mutation来修改state的数据。最后,根据state的变化,渲染到视图上。 -{% endhideToggle %} - -1. **vue中组件之间的通信方式?** -{% hideToggle 查看答案 %} - - `props ★★` - - 父组件 A 通过 props 向子组件 B 传递值, B 组件传递 A 组件通过 $emit A 组件通过 v-on/@ 触发 - - 子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。 - - `$emit/$on 事件总线 ★★` - - [vue-bus-ts](https://leedebug.github.io/2020/11/07/%E4%B8%AD%E5%A4%AE%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BA%BF%E6%8F%92%E4%BB%B6vue-bus-ts/) - - `vuex ★★★` - - 结合`localStorage`保存登录信息及权限列表等 - - `$parent/$children` - - `$attrs/$listeners` - - 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法。 - - `$attrs`:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。 - - `$listeners`: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v- on="$listeners" 传入内部组件 - - `provide/inject ★★★` - - 优点:使用简单 缺点:不是响应式 - - 父级:`provide: { name: '王者峡谷' //这种绑定是不可响应的 }`(`name: this`会有响应式,把当前组件实例传递下去,但子组件会绑定一些多余的属性,比如props、methonds等) - - 子级:`inject: ['name'] }` -{% endhideToggle %} - -1. **vue-router 中的导航钩子由那些?** - - 源码: `compiler` - -2. **什么是递归组件?** - - 源码: `compiler` - -3. **说一说vue响应式理解?** - - 源码: `compiler` - -4. **vue如果想要扩展某个组件现有组件时怎么做?** - - 源码: `compiler` - -5. **vue为什么要求组件模版只能有一个根元素?** - - 源码: `compiler` - -6. **watch和computed的区别以及怎么选用?** - - 源码: `compiler` - -7. **你知道nextTick的原理吗?** - - 源码: `compiler` - -8. **你知道vue双向数据绑定的原理吗?** - - 源码: `compiler` - -9. **简单说一说vue生命周期的理解?** - - 源码: `compiler` - -10. **[vue.js的两大核心](https://www.cnblogs.com/qlongbg/p/13025274.html)** -{% hideToggle 查看答案 %} - - 数据驱动,也就是数据的双向绑定 - - 组件系统 - - 模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。 - - 初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。 - - 接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。 - - 方法(methods):对数据的改动操作一般都在组件的方法内进行。 - - 生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,最新2.0版本对于生命周期函数名称改动很大。 - - 私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。 -{% endhideToggle %} -1. **[什么是MVVM](https://juejin.im/entry/6844903491886907399)(操作数据,就是操作视图,就是操作DOM)** -2. **MVVM的优点:** -{% hideToggle 查看答案 %} - - 分离视图(View) 和模型(Model) ,降低代码耦合,提高视图或者逻辑的重用性:比如视图(View) 可以独立于Model|变化和修改,-个ViewModel可以绑定不同的"View"上,当View变化的时候Model不可以不变,当Model|变化的时候View也可以不变。你可以把一些视图逻辑放在 -个ViewModel里面, 让很多view重用这段视图逻辑 - - 提高可测试性:ViewModel的存在可以帮助开发者更好地编写测试代码 - - 自动更新dom:利用双向绑定,数据更新后视图自动更新,让开发者从繁琐的手动dom中解放 -{% endhideToggle %} -1. **MVVM的缺点:** -{% hideToggle 查看答案 %} - - Bug很难被调试:因为使用双向绑定的模式,当你看到界面异常了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的 - - 一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存 - - 对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高 -{% endhideToggle %} -1. **你怎么理解vue中的diff算法?** -{% hideToggle 查看答案 %} - - diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为0(n)。 - - vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方。 - - vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上-次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch。 - - diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。 -{% endhideToggle %} -1. **谈一谈对vue组件化的理解?** -{% hideToggle 查看答案 %} - - 组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用; - - 组件化开发能大幅提高应用开发效率、测试性、复用性等; - - 组件使用按分类有:页面组件、业务组件、通用组件; - - vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue; - - vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等; - - 合理的划分组件,有助于提升应用性能; - - 组件应该是高内聚、低耦合的; - - 遵循单向数据流的原则。 -{% endhideToggle %} -1. **谈一谈对vue设计原则的理解?** -2. **谈谈你对MVC、MVP和MVVM的理解?** -3. **你了解哪些Vue性能优化方法?** -4. **什么叫发布订阅模式** -5. **如何实现发布订阅模式** -6. **proxy和object.definepropoty的区别** -7. **proxy的应用场景** -8. **你对虚拟dom和diff算法的理解,实现render函数** -9. **绑定this的几种方式** -10. **setState是同步还是异步的** - -## 双向绑定 - -1. **双向绑定的原理(差点让我手撸一个双向绑定)[Object.defineProperty()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)和[Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)** -{% hideToggle 查看答案 %} - - Vue 采用[`数据劫持 结合 发布者-订阅者模式`](https://www.jianshu.com/p/1032ecd62b3a)的方式来实现数据的响应式,通过Object.defineProperty(点我查看该属性)来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。 - - Observer:数据的观察者,让数据对象的读写操作都处于自己的监管之下。当初始化实例的时候,会递归遍历data,用Object.defineProperty来拦截数据(包含数组里的每个数据)。 - - Dep:数据更新的发布者,get数据的时候,收集订阅者,触发Watcher的依赖收集;set数据时发布更新,通知Watcher 。 - - Watcher:数据更新的订阅者,订阅的数据改变时执行相应的回调函数(更新视图或表达式的值)。 - - 图中红色的箭头表示的是收集依赖时获取数据的流程。Watcher会收集依赖的时候(这个时机可能是实例创建时, 解析模板、初始化watch、初始化computed,也可能是数据改变后,Watcher执行回调函数前),会获取数据的值,此时Observer会拦截数据 (即调用get函数),然后通知Dep可以收集订阅者啦。Dep将订阅数据的Watcher保存下来,便于后面通知更新。 - - 图中绿色的箭头表示的是数据改变时,发布更新的流程。当数据改变时,即设置数据时,此时Observer会拦截数据(即调用set函数),然后通知Dep,数据改变了,此时Dep通知Watcher,可以更新视图啦。 -{% endhideToggle %} -1. **vue2中对于数组的变化检测是重写数组方法:** -{% hideToggle 查看答案 %} - - 'push' - - 'pop' - - 'shift' - - 'unshift' - - 'splice' - - 'sort' - - 'reverse' -{% endhideToggle %} -1. **你知道vue中key的作用和工作原理吗?说说你对它的理解。** -{% hideToggle 查看答案 %} - - 源码中找答案:src\core\vdom\patch.js - updateChildren() - - key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中调用updateChildren(),利用patchVNode()方法,通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量提高性能。 - - 如果不适用key的时候,在执行patchVNode()之前会一直判断sameVNode()并返回true,因为不设置key的话标签的key会默认为undefined,此时undefined===undefined,所以会判断为同一个节点。 - - 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug - - vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。 -{% endhideToggle %} -1. **你是如何理解Vue的响应式系统的?** -{% hideToggle 查看答案 %} - - 任何一个Vue Component都有一个与之对应的Watcher实例。 - - Vue的data上的属性会被添加getter和setter属性。 - - 当Vue Component render函数被执行的时候,data. 上会被触碰(touch),即被读,getter方法会被调用,此时Vue会去记录此Vuecomponent所依赖的所有data。(这-过程被称为依赖收集) - - data被改动时(主要是用户操作),即被写,setter方法会被调用,此时Vue会去通知所有依赖于此data的组件去调用他们的render函数进行更新。 -{% endhideToggle %} -1. **既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虛拟DOM进行diff检测差异?** -{% hideToggle 查看答案 %} - - 考点: Vue的变化侦测原理 - - 前置知识:依赖收集、虚拟DOM、响应式系统 - - 现代前端框架有两种方式侦测变化,-种是pull-种是push - - push: Vue的响应式系统则是push的代表当Vue程序初始化的时候就会对数据data进行依赖的收集,-但数据发生变化,响应式系统就会立刻得知,因此Vue是-开始就知道是「在哪发生变化了」,但是这又会产生一一个问题,如果你熟悉Vue的响应式系统就知道通常一个绑定-个数据就需要一个Watcher,-但我们的绑定细粒度过高就会产生大量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择`中等细粒度`的方案,在`组件级别`进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom Diff 获取更加具体的差异,而`Virtual Dom Diff 则是pull操作`,Vue是`push+pull结合`的方式进行变化侦测的. - - pull:其代表为React,我们可以回忆一下React是如何侦测到变化的,我们通常会用setStateAPI显式更新,然后React会进行一层层的Virtual Dom Diff操作找出差异然后Patch到DOM上,React从一-开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的iff操作查找「哪发生变化了」,另外-个代表就是Angular的脏检查操作。 -{% endhideToggle %} - -## 生命周期 - -1. **如何从零开始初始化vue项目** -2. **生命周期是什么** -{% hideToggle 查看答案 %} - - Vue实例有一个完整的生命周期,也就是从 - - 开始创建、初始化数据、编译模版、挂载Dom->渲染、更新-> 渲染、卸载等-系列过程,我们称这是Vue的生命周期。 -{% endhideToggle %} -1. **[简单说一说vue生命周期的理解?](https://www.axihe.com/focus/vue/08.html)** -{% hideToggle 查看答案 %} - - beforeCreate:在实例初始化之后,数据观测(data observe)和event/watcher事件配置之前被调用,这时无法访问data及props等数据; - - created:在实例创建完成后被立即调用,此时实例已完成数据观测(data observer),属性和方法 的运算,watch/event事件回调,挂载阶段还没开始, $el 尚不可用。 - - beforemount:在挂载开始之前被调用,相关的render函数首次被调用。 - - mounted:实例被挂载后调用,这时el被新创建的vm. $el 替换,若根实例挂载到了文档上的元素上,当mounted被调用时vm.$el也在文档内。注意mounted不会保证所有子组件一起挂载。 - - beforeupdata:数据更新时调用,发生在虚拟dom打补丁前,这时适合在更新前访问现有dom,如手动移除已添加的事件监听器。 - - updated:在数据变更导致的虚拟dom重新渲染和打补丁后,调用该钩子。当这个钩子被调用时,组件dom已更新,可执行依赖于dom的操作。多数情况下应在此期间更改状态。 如需改变,最好使用watcher或计算属性取代。注意updated不会保证所有的子组件都能一起被重绘。 - - beforedestory:在实例销毁之前调用。在这时,实例仍可用。 - - destroyed:实例销毁后调用,这时vue实例的所有指令都被解绑,所有事件监听器被移除,所有子实 例也被销毁。 - - activated:(新增钩子)keep-alive 组件激活时调用。 类似 created 没有真正创建,只是激活 - - deactivated:(新增钩子)keep-alive 组件停用时调用。类似 destroyed 没有真正移除,只是禁用 -{% endhideToggle %} -1. **[Vue.js的八大生命周期](https://www.axihe.com/focus/vue/08.html)** -2. **[Vue.js的父子组件生命周期调用顺序](https://www.axihe.com/focus/vue/23.html)** -3. **[Vue.js的ajax请求放在哪个生命周期中](https://www.axihe.com/focus/vue/09.html)** -4. **你知道nextTick的原理吗?** -{% hideToggle 查看答案 %} - - (唯一能监听到DOM改动的API:MutationObserver,HTML5新增的属性,用于监听DOM修改事件,能够监听到节点的属性、文本内容、子节点等的改动,是一个功能强大的利器。) - - `事件循环(Event Loop)`,浏览器 - - `任务队列(task queues)` - - `微任务队列(MicroTask_Queue)`,是做队列控制的最佳选择。每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。在执行microtask的过程中后加入microtask队列的微任务,也会在下一次事件循环之前被执行。也就是说,macrotask总要等到microtask都执行完后才能执行,microtask有着更高的优先级。 - - `常见的microtask`有:Promise、MutationObserver、Object.observe(废弃),以及nodejs中的 process.nextTick. - - `vue的降级策略`,队列控制的最佳选择是microtask,而microtask的最佳选择是Promise。但如果当前环境不支持Promise,vue就不得不降级为macrotask来做队列控制了。 - - 在vue2.5的源码中,macrotask降级的方案依次是:setImmediate、MessageChannel、setTimeout. -{% endhideToggle %} - -## data - -1. **Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?** -{% hideToggle 查看答案 %} - - 源码中找答案:src\core\instance\state.js - initData() - - `Vue组件`可能存在`多个实例`,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。而在`Vue根实例`创建过程中则不存在该限制,也是因为根实例只能有一个,即`单例`存在,不需要担心这种情况。 -{% endhideToggle %} -1. **如果改了data的多个值update钩子会执行几次,为什么** - -## computed && watch - -1. **Vue computed 的实现原理** -2. **Vue watch 的实现原理** -3. **computed和watch的区别和应用** - - -## vuex - -1. **[vuex是什么](https://vuex.vuejs.org/zh/)、[vuex的源码](https://unpkg.com/vuex@3.5.1/dist/vuex.js)、底层(Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue)、使用场景(组件通信、登录状态、加入购物车、公共码表维护、页面缓存(慎用))** -2. **[mutation的用法](https://vuex.vuejs.org/zh/guide/mutations.html)** - -## vue-router - -1. **Vue-router 的实现原理** -2. **vue的两种路由模式,问我这两种路由模式的底层实现(昨天刚复习过 美滋滋)** -3. **vue-router中push和replace的区别** -4. **[vueRouter的钩子函数](https://zhuanlan.zhihu.com/p/70536937)** - - 全局的路由钩子函数:beforeEach(全局前置守卫)、afterEach(全局后置守卫) - - 路由配置文件独享的钩子函数:beforeEnter - - 组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate -5. **[vue-router中的导航钩子由那些?](https://zhuanlan.zhihu.com/p/70536937)** - - 全局的路由钩子函数:beforeEach(全局前置守卫)、afterEach(全局后置守卫) - - 路由配置文件独享的钩子函数:beforeEnter - - 组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate -6. **如何(动态)控制路有权限,颗粒多大** -7. **[vue项目实现路由按需加载(路由懒加载)的3种方式](https://blog.csdn.net/xm1037782843/article/details/88225104)** - - vue的异步组件技术:`component: resolve => require(['@/components/home'],resolve)` - - es提案的import():`const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')`。有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用命名chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。 - - webpack提供的require.ensure():`r => require.ensure([], () => r(require('@/components/index')), 'demo')` -8. **[完整的导航解析流程](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%AE%8C%E6%95%B4%E7%9A%84%E5%AF%BC%E8%88%AA%E8%A7%A3%E6%9E%90%E6%B5%81%E7%A8%8B)** - - 导航被触发。 - - 在失活的组件里调用离开守卫。 - - 调用全局的 beforeEach 守卫。 - - 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 - - 在路由配置里调用 beforeEnter。 - - 解析异步路由组件。 - - 在被激活的组件里调用 beforeRouteEnter。 - - 调用全局的 beforeResolve 守卫 (2.5+)。 - - 导航被确认。 - - 调用全局的 afterEach 钩子。 - - 触发 DOM 更新。 - - 用创建好的实例调用 beforeRouteEnter - 守卫中传给 next 的回调函数。 - -## 组件通信 - -1. **[Vue.js组件如何通信以及有哪些方式?](https://www.axihe.com/focus/vue/24.html)** -2. **vue中如何实现兄弟组件通信:** - - EventBus(事件总线),即一个新的vue实例。 - - 使用方法:export const eventBuss = new Vue(),然后event.$emit()调用,created中使用event.$on()监听 -3. **vue如果想要扩展某个组件现有组件时怎么做?** - - mixin或者slots -4. **什么是递归组件?** - - 组件自身调用自身,但一定要有终止条件 - -## directives - -1. **[v-on事件修饰符及按键修饰符](https://blog.csdn.net/sleepwalker_1992/article/details/82903246)** - - `.stop`:阻止事件冒泡 - - `.self`:当事件在该元素本身触发时才触发事件 - - `.capture`:添加事件侦听器是,使用事件捕获模式 - - `.prevent`:阻止默认事件 - - `.once`:事件只触发一次 - - 下面为,按键修饰符 - - `@click.stop.prevent="btnClick"`:既阻止了默认事件,又阻止了事件冒泡 - - `@keyup.enter.native="handleRegister"`或`@keyup.13="submit"`:监听回车事件 -2. **v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?** - - 源码中找答案`compiler/codegen/index.js` - - 显然v-for优先于v-if被解析(把你是怎么知道的告诉面试官,比如打印渲染函数console.log(app.$options.render);或源码 else if 判断先v-for再v-if) - - 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能 - - 要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环 - - 如果条件出现在循环内部,可通过计算属性`提前过滤掉`那些不需要显示的项 -3. **[vue自定义指令和私有指令](https://cn.vuejs.org/v2/guide/custom-directive.html)** -4. **[delete和Vue.delete(this.$delete)删除数组的区别](https://www.jianshu.com/p/fbe96ed71fd7)** - - var a=[1,2,3,4] - - var b=[1,2,3,4] - - delete a[1] - - console.log(a) // [1,empty,2,3] - - this.$delete(b,1) - - console.log(b) // [1,3,4] -5. **v-model的作用和原理** -6. **v-show和v-if的区别,与dom操作是否相关** -7. **如何取到子组件的实例,除了$ref还有无别的方法** - -## others - -1. **vue中css处scoped的实现原理及穿透的用法** -2. **[video的层级](https://www.jianshu.com/p/79100d43939a)(他的层级是最高的,问我怎么在video上添加一些div,我之前有看过aliplayer这个插件,面试官也很满意)** -3. **[30行写一个Vue图片懒加载指令](https://mp.weixin.qq.com/s/vtg6DaGKdslHQZUTpDiC7w)** - - - - - - -# Vue 3.x - -1. **[vue3.x的新特性研究](https://blog.csdn.net/sky_cmc/article/details/104988921)** - - Emm...,慢慢看 - -2. **[你对Vue3.0的新特性有没有了解?](https://www.yuque.com/woniuppp/vue3/feature)** -3. **vue2和vue3的特点和新特性** -4. **Proxy与Object.defineProperty的优劣对比?** - - Proxy的优势如”下: - - Proxy可以直接监听对象而非属性 - - Proxy可以直接监 听数组的变化 - - Proxy有多达13种拦截方法,不限于apply、ownKeys、 deleteProperty、 has等 等是object . defineProperty不具备的 - - Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而object.defineProperty只能遍历对象属性直接修改 - - Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利 - - Object.defineProperty的优势如”下: - - 兼容性好,支持IE9 - - - - -# Webpack -- [webpack基础配置](https://www.webpackjs.com/configuration/): - - context:基础目录,绝对路径,用于从配置中解析入口起点(entry point)和 loader - - entry:起点或是应用程序的起点入口。从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。 - - output:位于对象最顶级键(key),包括了一组选项,指示webpack如何去输出、以及在哪里输出你的「bundle、asset和其他你所打包或使用webpack载入的任何内容」。比如path: config.build.assetsRoot - - resolve:extensions: ['.js', '.vue', '.json'],自动解析确定的扩展。 - - 模式(mode):告知 webpack 使用相应模式的内置优化。development、production -- [webpack的工作原理和流程](https://blog.csdn.net/weixin_43334673/article/details/107598708) - - Webpack CLI 启动打包流程; - - 载入 Webpack 核心模块,创建 Compiler 对象; - - 使用 Compiler 对象开始编译整个项目; - - 从入口文件开始,解析模块依赖,形成依赖关系树; - - 递归依赖树,将每个模块交给对应的 Loader 处理; - - 合并 Loader 处理完的结果,将打包结果输出到 dist 目录。 -- [webpack的构建流程](https://segmentfault.com/a/1190000021494964?utm_source=tag-newest) - - `初始化参数`:从配置文件和Shell语句中读取与合并参数,得出最终的参数。 - - `开始编译`:用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始编译整个项目。 - - `确定入口`:根据配置中的entry找出所有的入口文件。 - - `编译模块`:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。 - - `完成模块编译`:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的`依赖关系树`。 - - `输出资源`:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。 - - `输出完成`:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统即dist目录。 -- [webpack基础知识](https://juejin.im/post/6855129007785328653#heading-15) -- [wepack中loader和plugin的区别](https://blog.csdn.net/jiang7701037/article/details/98887179?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param) - - `功能作用`的角度区分: - - loader:直译为“加载器”,用于加载某些非js资源文件。这让webpack有了`加载和解析非js文件`的能力。因为webpack本身只能打包commonjs规范的js文件,对于其他资源例如css,图片,或者其他的语法集,比如jsx,coffee,是没有办法加载的。这就需要对应的loader将资源转化,加载进来。从字面意思,也能看出,loader是用于加载的,它作用于一个个文件上。 - - plugin:直译为“插件”,用于扩展webpack的功能。它直接作用于webpack,扩展了它的功能。在webpack的运行生命周期中会广播许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。 - - `运行时机`的角度区分: - - loader:运行在打包文件之前(loader为在模块加载时的预处理文件) - - plugins:在整个编译周期都起作用。 - - `配置方法`的角度区分: - - loader:在`module.rules`中配置,也就是说他作为模块的解析规则存在。类型为数组,每一项都是一个object,里面描述了对于什么类型的文件(test),使用了什么加载(loader)和参数(options) - - plugins:在plugins中单独配置。类型为数组,每一项都是一个plugin的实例,参数他通过构造函数传入。 -- [常见的、用过的loader](https://www.webpackjs.com/loaders/): - - 文件:`file-loader`将文件发送到输出文件夹,并返回(相对)URL,png|jpg|gif、svg、mp4 - - JSON:`json-loader`加载 JSON 文件(默认包含) - - 转换编译(Transpiling):`script-loader`在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析 - - 转换编译(Transpiling):`babel-loader`加载 ES2015+ 代码,然后使用 Babel 转译为 ES5 - - 模板(Templating):`html-loader`导出 HTML 为字符串,需要引用静态资源 - - 样式:`css-loader`解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码 - - 样式:`style-loader`将模块的导出作为样式添加到 DOM 中 - - 样式:`less-loader`加载和转译 LESS 文件 - - 样式:`sass-loader`加载和转译 SASS/SCSS 文件 - - 清理和测试(Linting && Testing):`eslint-loader`,PreLoader,使用 ESLint 清理代码 - - 框架(Frameworks):`vue-loader`加载和转译 Vue 组件 -- [常见的、用过的plugin](https://www.webpackjs.com/plugins/): - - `DefinePlugin`允许创建一个在编译时可以配置的全局常量('process.env': require('../config/dev.env')) - - `WebpackBar`显示打包/启动可视化进度条 - - `mini-css-extract-plugin`是可以提取CSS到单独的文件中 - - `HtmlWebpackPlugin`简化了HTML文件的创建 - - `webpack.HotModuleReplacementPlugin()`启用热替换模块插件 - - `CopyWebpackPlugin`将单个文件或整个目录复制到生成目录(from:path.resolve(__dirname, '../static'),to:config.build.assetsSubDirectory,ignore: ['.*']) -- 谈谈你对webpack的看法 - - WebPack 是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、JavaScript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源。 -- webpack的两大特色: - - code splitting(代码拆分)[按需加载](https://www.jianshu.com/p/b3b8fb8a2336) - - ==loader==:可以处理各种类型的静态文件,并且支持串联操作 -- webpack是以[commonJS](https://www.w3cschool.cn/zobyhd/1ldb4ozt.html)的形式来书写脚本滴,但对[AMD/CMD](https://github.com/seajs/seajs/issues/277)的支持也很全面,方便旧项目进行代码迁移。 -- webpack具有requireJs和browserify的功能,但仍有很多自己的新特性: - - 对CommonJS、AMD、ES6的语法做了兼容 - - 对js、css、图片等资源文件都支持打包 - - 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持 - - 有独立的配置文件webpack.config.js - - 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间 - - 支持SourceUrls和[SourceMaps](http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html),易于调试 - - 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活 - - webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快 -- webpack、vint、lua -- [Vue首屏加载慢的优化方案](https://www.jianshu.com/p/b73f4aee0f1d) - - `vendor.js过大问题解决`: - - 在index.html中使用CDN的资源 - - 在bulid/webpack.base.conf.js文件中添加externals - - 在main.js里将以下import注释替换require引入模块 - - `vue-cli开启打包压缩和后台配合gzip访问` - - npm install --save-dev compression-webpack-plugin@1.1.11 - - 打开 config/index.js ,找到 build 对象中的 productionGzip ,改成 true - - 此时打包的文件会 新增 .gz 文件 - - 后台nginx开启gzip模式访问(gzip on;),浏览器访问项目,自动会找到 .gz 的文件 -- 如何用webpack来优化前端性能?(用webpack优化前端性能是指优化webpack的`输出结果`,让打包的最终结果在浏览器运行快速高效。) - - `压缩代码`:删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的uglifyJsPlugin和ParalleluglifyPlugin来压缩JS文件,利用cssnano (css-loader?minimize) 来压缩css,利用` babel-plugin-transform-remove-console`删掉console代码 - - `利用CDN加速`:在构建过程中,将引用的静态资源路径修改为CDN.上对应的路径。可以利用webpack对于output参数和各loader的publicPath 参数来修改资源路径 - - `Tree Shaking`:将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现 - - `Code Splitting`:将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存 - - `提取公共第三方库`:SplitChunksPlugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码 -- 如何提高webpack的打包速度? - - `happypack`:利用进程并行编译loader,利用缓存来使得rebuild更快,遗憾的是作者表示已经不会继续开发此项目,类似的替代者是thread-loader - - `外部扩展(externals)`:将不怎么需要更新的第三方库脱离webpack打包,不被打入bundle中,从而减少打包时间,比如jQuery用script标签引入 - - `dll`:采用webpack的DIIPlugin和DIlReferencePlugin引入dll,让-些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间 - - `利用缓存`:webpack.cache、babel-loader.cacheDirectory、HappyPack.cache都可以利用缓存提高rebuild效率 - - `缩小文件搜索范围`:比如babel-loader插件,如果你的文件仅存在于src中,那么可以include: path.resolve(_dirname,'sre'),当然绝大多数情况下这种操作的提升有限,除非不小心build了node-modules文件 -- 如何提高webpack的构建速度? - - 多入口情况下,使用CommonsChunkPlugin来提取公共代码 - - 通过externals配置来提取常用库 - - 利用D1IPlugin和Dl1ReferencePlugin预编译资源模块通过D11Plugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。 - - 使用Happypack实现多线程加速编译 - - 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度 - - 使用Tree-shaking和ScopeHoisting来剔除多余代码 -- 怎么配置多页应用? - - 可以使用webpack的AutowebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。要注意的是: - - 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表 - - 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置 - - - - - - -# HTTP(S) -- HTTP2相对于HTTP1.x有什么优势和特点? - - 二进制分帧:HTTP/2采用二进制格式传输数据,而非HTTP 1.x的文本格式,二进制协议解析起来更高效。 - - 帧: HTTP/2 数据通信的最小单位消息:指HTTP/2中逻辑上的HTTP消息。例如请求和响应等,消息由一个或多个帧组成。 - - 流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID - - 头部压缩:HTTP2使用“首部表”,只发送差异数据,而不是全部发送,从而减少头部的信息量。 - - HTTP/1.x会在请求和响应中中重复地携带不常改变的、冗长的头部数据,给网络带来额外的负担。 - - HTTP/2在客户端和服务器端使用“首部表"来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送 - - 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新 - - 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。 -- http的几个状态码,比如:304、200、500、502、504等 - - 2XX成功 - - 200 OK,表示从客户端发来的请求在服务器端被正确处理 - - 201 Created请求已经被实现,而且有-个新的资源已经依据请求的需要而建立 - - 202 Accepted请求已接受,但是还没执行,不保证完成请求 - - 204 No content,表示请求成功,但响应报文不含实体的主体部分 - - 206 Partial Content, 进行范围请求 - - 3XX重定向 - - 301 moved permanently,永久性重定向,表示资源已被分配了新的URL - - 302 found, 临时性重定向,表示资源临时被分配了新的URL(302是http1.0的协议状态码,在http1.1版本的时候为 了细化302状态码又出来了两个303和307) - - 303 see other, 表示资源存在着另一个URL,应使用GET方法J香获取资源(303明确表示客户端应当采用get方法获取资源,他会把POST请求变为GET请求进行重定向) - - 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况 - - 307 temporary redirect,临时重定向,和302含义相同(307会遵照浏览器标准,不会从post变为get) - - 4XX客户端错误 - - 400 bad request,请求报文存在语法错误 - - 401 unauthorized,表示发送的请求需要有通过HTTP认证的认证信息 - - 403 forbidden,表示对请求资源的访问被服务器拒绝 - - 404 not found,表示在服务器.上没有找到请求的资源 - - 408 Request timeout,客户端请求超时 - - 409 Confict,请求的资源可能引起冲突 - - 5XX服务器错误 - - 500 internal sever error,表示服务器端在执行请求时发生了错误 - - 501 Not Implemented请求超出服务器能力范围,例如服务器不支持当前请求所需要的某个功能,或者请求是服务 -器不支持的某个方法 - - 503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求 - - 505 http version not supported服务器不支持,或者拒绝支持在请求中使用的HTTP版本 -- [关于HTTP协议,一篇就够了](https://www.cnblogs.com/ranyonsue/p/5984001.html) -- [浏览器缓存机制](https://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html) - - [缓存机制优先级](https://juejin.im/post/6854573215830933512#heading-40):ETag > Last-Modified > Expires(HTTP1.0的东西) - - Etag + If-None-Match - - Last-Modified + If-Modified-Since - - [http之Access-Control-Max-Age](https://blog.csdn.net/john1337/article/details/78928851) -- [http的状态码200(强缓存)和304(协商缓存)有什么区别](https://www.jianshu.com/p/fb59c770160c) - - 强缓存:直接从本地副本比对读取,不去请求服务器,返回的状态码是200。主要包括`expires`和`cache-control` - - 协商缓存:去服务器比对,若没改变才直接读取本地缓存,返回的状态码是304。主要包括`last-modified`和`Etag` -- 从输入URL到页面展现中间发生了什么 - - 域名解析 -> TCP连接(3次握手)-> 建立连接 -> HTTP请求 -> 后台处理请求 -> HTTP响应 -> 关闭连接 (4次挥手)-> 解析HTML -> 渲染 -- [图解tcp三次握手四次挥手](https://blog.csdn.net/Uranus1211/article/details/80463018) - - `可以四次握手`(确认报文段可以拆开发送,可以先发一个确认报文段:ack=x+1,ACK=1,再发送一个同步报文段:SYN=1,seq=y),但`不能两次握手` - - `可以三次挥手`(如果服务器收到FIN的时候,也正要关闭连接,就可以将FIN 和ACK一起发送过去) -- TCP传输的三次握手 - - 第一次:SYN=1(请求连接),seq=x(传输数据流序号) - - 第二次:SYN=1(接受连接),ack=x+1(ack=seq+1)(确认接受数据流),ACK=1(确认序号),seq=y(传输响应数据序号) - - 第三次:ack=y+1,ACK=1(确认序号),seq=x+1(因为第一次是x)。没有SYN,因为SYN这个标志位只有TCP建立连接时才被置为1。 -- TCP传输的四次挥手 - - 第一次,FIN=1,seq=i,表示主动断开连接请求 - - 第二次,ack=i+1,ACK=1,seq=j,表示确认收到信息,同时可能存在服务器没有将数据全部传输完成 - - 第三次,FIN=1,ack=j+1,ACK=1,seq=k,表示可以(被动)关闭连接,同时可能发送数据 - - 第四次,ack=k+1,ACK=1,表示确认收到信息,断开连接。 -- TCP和UDP的区别 - - TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议 - - UDP(User Data Protocol,用户数据报协议)是面向非连接的协议 -- HTTP和TCP协议是什么关系? - - 从OSI开放系统互连参考模型(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层)看 - - `TCP`属于`运输层`的协议,主要解决数据如何在网络中传输,负责提供应用进程之间的通信 - - `HTTP`属于`应用层`上的一种协议,主要解决如何包装数据,是上层的协议,需要下层TCP的支持 -- HTTPS为什么比较安全(这个关联性不是很大) - - HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个`安全协议层`(SSL或TSL。包含:证书、卸载、流量转发、负载均衡、页面适配、浏览器适配、refer传递等),这个时候就成了我们常说的HTTPS。 -- 创建ajax过程 - - 创建XMLHttpRequest对象,也就是创建一个异步调用对象. - - 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息. - - 设置响应HTTP请求状态变化的函数. - - 发送HTTP请求. - - 获取异步调用返回的数据. - - 使用JavaScript和DOM实现局部刷新. -- 如果一条请求返回了跨域提醒,服务器是否已经接收到(是,否则无法返回相应) - - - - - - -# 浏览器 -- [图解浏览器的基本工作原理](https://zhuanlan.zhihu.com/p/47407398) - - Browser Process(浏览器主进程):负责包括地址栏,书签栏,前进后退按钮等部分的工作;负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问 - - Renderer Process:负责一个 tab 内关于网页呈现的所有事情 - - Plugin Process:负责控制一个网页用到的所有插件,如 flash - - GPU Process:负责处理 GPU 相关的任务 -- [线程和进程的区别](https://mtech.fatiao.pro/detail/12756.html) -- [浅读V8——强大的JavaScript引擎](https://www.jianshu.com/p/332c15fd7c7d) - - V8的大致流程:JavaScript源代码 -> 抽象语法树(AST)-> 本地代码 - - JavaScriptCore:JavaScript源代码 -> 抽象语法树(AST)-> 字节码-> 本地代码 -- cookie是干嘛的?有什么用?session又是什么? -- 浏览器的缓存机制如何实现的 -- 怎么查看某个缓存的到期时间及大小 -- 如何控制开关浏览器缓存 -- 如何实现跨浏览器保存登录状态 -- 浏览器自带多少进程 -- 浏览器自带多少线程 - - - - - - -# WebSocket -- [WebSocket](http://www.ruanyifeng.com/blog/2017/05/websocket.html)协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。是Web应用程序的传输协议,它提供了双向的,按序到达的数据流。他是一个Html5协议,WebSocket的连接是持久的,他通过在客户端和服务器之间保持双工连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。 -- websocket(stomp.js+socket.js) - - - - - - -# 安全 -- 常见web安全及防护原理 - - sql注入原理:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。 - - 1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。 - - 2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。 - - 3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。 - - 4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。 - - Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。 - - XSS防范方法: - - 尽量采用POST而非GET提交表单 - - 避免直接在cookie中泄露用户隐私,例如email、密码等等 - - 通过使cookie和系统ip绑定来降低cookie泄露后的危险。这样攻击者得到的cookie没有实际价值,不可能拿来重放 - - 如果网站不需要再浏览器端对cookie进行操作,可以在Set-Cookie末尾加上HttpOnly来防止javascript 代码直接获取cookie -- XSS与CSRF有什么区别吗? - - XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。 - - CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。(登录受信任网站A,并在本地生成Cookie;在不登出A的情况下,访问危险网站B。) -- CSRF的防御 - - 服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。 - - 通过验证码的方法 -- [XSS和CSRF攻击原理浅谈](https://blog.csdn.net/hopefullman/article/details/87817193) -- [XSS与CSRF攻击的详解与区别](https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/104148444) -- XSS攻击的解决办法: - - `网站的Cookie设置HttpOnly属性` - - html escape 转义 - - 后端永远不要相信前端的数据,服务端的输出检查。 -- CSRF攻击的解决办法: - - `Token 验证` - - `Referer Check`:根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该 HTTP 请求的来源地址。 - - 验证码 - - 尽量不要在页面的链接中暴露用户隐私信息。 - - 对于用户修改删除等操作最好都使用post 操作 。 -- 如何解决xss攻击(慎用v-html、过滤bmp图片、我说了后端请求头加一个属性设置为true即可,具体什么属性我忘了,还有一种是字符串过滤,过滤script标签即可) - - - - - - -# TypeScript -- 有哪些操作符?Typeof 是干嘛的? -- TS引入一个JS模块? -- Declare 关键字是干嘛的? -- 什么是类型保护,有什么用?如何触发类型保护? -- Never 类型有什么用? - - - - - -# Git -- Git rebase 和 merge 的区别 -- docker实现原理及部署命令 - - - - -# 实战 -1. **编程:编译一下模板字符串** -2. **编程:实现音频和动画的onStart和onEnd异步控制** -3. **编程:手动实现new运算符** -4. **编程:手动实现instanceof运算符** -5. **编程:手写parseInt** - - - - -# 前端思想 -- 如何理解前端工程化 -- [webpack做过哪些优化](https://juejin.im/post/6855129007785328653) -- 如何实现前端性能优化 -- 你觉得前端工程的价值体现在哪 - - 为简化用户使用提供技术支持(交互部分) - - 为多个浏览器兼容性提供支持 - - 为提高用户浏览速度(浏览器性能)提供支持 - - 为跨平台或者其他基于webkit或其他渲染引擎的应用提供支持 - - 为展示数据提供支持(数据接口) -- 谈谈性能优化问题 -- 渐进增强和优雅降级 - - 渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。 - - 优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。 -- [前端的可视化库](https://zhuanlan.zhihu.com/p/47154189):ECharts、AntV - - - - -# 全栈 -- 一个Web应用是怎样运作的,如何优化 - - - - -# 小程序 -- 小程序里面存在域的概念吗 -- 小程序时候踩过哪些坑 - - - - -# Django -- [django分为哪几层:FBC和VBC???](https://www.cnblogs.com/rexcheny/p/11207630.html) -- 后台的登录是怎么做的 -- [Django框架的运行方式及处理流程](https://www.jianshu.com/p/e122a56b6b19) -- 如何判断一个用户是否登录了 -- session存在哪 - - - - -# Sql -- [sql的聚合api有哪些](https://blog.csdn.net/qq_32486599/article/details/73655817):count、sum、max、min - - - - -# 数据结构 -- [数组和链表的区别和优缺点总结](https://blog.csdn.net/weibo1230123/article/details/82011889) - -特性\结构 | 数组 | 链表 ----|---|--- -元素个数 | 固定 | 按需增减 -存储单元 | 定义时分配,要连续 | 程序执行时动态向系统申请,无需连续 -元素顺序 | 由位置(下标)确定 | 由指针指向确定 -元素增减 | 会频繁移动元素 | 改变指针指向即可 -查找效率 | 随机读取效率很高 | 不可随机访问,只能按序 -扩展性能 | 空间不够时要重新定义数组 | 链表大小不用定义,数据随意增删 - - - - -# 算法 - -排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 ----|---|---|---|---|---|--- -==冒泡排序== | **O(n^2^)** | O(n) | O(n^2^) | O(1) | In-Place | **稳定** -选择排序 | O(n^2^) | O(n^2^) | O(n^2^) | O(1) | In-Place | 不稳定 -插入排序 | O(n^2^) | O(n) | O(n^2^) | O(1) | In-Place | 稳定 -希尔排序 | O(n log n) | O(n log^2^ n) | O(n log^2^ n) | O(1) | In-Place | 不稳定 -归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | Out-Place | 稳定 -==快速排序== | **O(n log n)** | O(n log n) | O(n^2^) | **O(log n)** | In-Place | **不稳定** -堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | In-Place | 不稳定 -计数排序 | O(n + k) | O(n + k) | O(n + k) | O(k) | Out-Place | 稳定 -桶排序 | O(n + k) | O(n + k) | O(n^2^) | O(n + k) | Out-Place | 稳定 -基数排序 | O(n * k) | O(n * k) | O(n * k) | O(n + k) | Out-Place | 稳定 - -### 冒泡排序(Bubble Sort) - -- 时间复杂度:O(n^2^), 最好O(n),最坏O(n^2^) -- 空间复杂度:O(1) -- 实现思路: - - 比较相邻的元素。如果第一个比第二个大, 就交换他们两个。 - - 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 - - 针对所有的元素重复以上的步骤,除了最后一个。 - - 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 - -```js -function bubbleSort(arr) { - for (var i = 0; i < arr.length; i++) { - for (var j = arr.length - 1; j > i; j--) { - if (arr[j] < arr[j - 1]) { - [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]] // 交换操作 - } - } - } - return arr -} -``` - -- 改进1:设置-标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。 - -```js -function bubbleSort2(arr) { - var i = arr.length - 1 // 初始化,最后的交换位置,保持不变 - while (i > 0) { - var pos = 0 // 每趟开始时,无记录交换 - for (var j = 0; j < i; j++) { - if (arr[j] > arr[j + 1]) { - pos = j // 记录交换的位置 - [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]] // 交换操作 - } - } - i = pos // 为下一趟排序做准备 - } - return arr -} -``` - -- 改进2:传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法,一次可以得到两个最终值(最大者和最小者),从而使排序趟数几乎减少了一半。 - -```js -function bubbleSort3(arr) { - var low = 0 - var high = arr.length - 1 // 设置变量初始值 - var tmp, j - while (low < high) { - for (j = low; j < high; ++j) { // 正向冒泡,找到最大值 - if (arr[j] > arr[j + 1]) { - [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]] // 交换操作 - } - } - --high // 前移一位 - for (j = high; j >low; --j) { // 反向冒泡,找到最小值 - if (arr[j]= pivot - j-- - } - if (i <= j) { - swap(array, i, j) - i++ - j-- - } - } - return i - } - - // 快速排序主函数 - function quick(array, left, right) { - let index - if (array.length > 1) { - index = partition(array, left, right) - if (left < index - 1) { - quick(array, left, index - 1) - } - if (index < right) { - quick(array, index, right) - } - } - return array - } - - // 调用主函数 - return function quickSort(array) { - return quick(array, 0, array.length - 1) - } - -})() -``` - -- 阮一峰的思路: - -```js -const quickSort = function(arr) { - if (arr.length <= 1) { - return arr - } - var pivotIndex = Math.floor(arr.length / 2) - var pivot = arr.slice(pivotIndex, 1)[0] - var left = [] - var right = [] - for (let i = 0; i <= arr.length; i++) { - if (arr[i] < pivot) { - left.push(arr[i]) - } else { - right.push(arr[i]) - } - } - return quickSort(left).concat([pivot], quickSort(right)) -} -``` - -### 二分查找(Binary Search) - -- [6种二分查找及其变式总结及牢记方法](https://blog.csdn.net/qq_42873341/article/details/86897987) -- 实现思路: - - 折半查找算法要求查找表的数据是线性结构存储,还要求查找表中的顺序是`由小到大排序(由大到小排序)` - - 首先设两个指针,left和right, 表示最低索引和最高索引 - - 然后取中间位置索引mid,判断mid处的值是否与所要查找的数相同,相同则结束查找,mid处的值比所要查找的值小就把left设为mid+1,如果mid处的值比所要查找的值大就把right设为mid-1 - - 然后再新区间继续查到,直到找到或者left>right找不到所要查找的值结束查找 - -```js -var binarySearch = function (arr, i) { - var left = 0 - var right = arr.length - 1 - while (left <= right) { - var mid = Math.floor((left + right) / 2) - if (i < arr[mid]) { - right = mid - 1 - } else if (i > arr[mid]) { - left = mid + 1 - } else if (i === arr[mid]) { - return mid - } - } - return false -} -``` - -### 深度优先遍历(DFS) - -- [深度优先遍历(DFS)和广度优先遍历(BFS)](https://www.jianshu.com/p/b4d8085e84bd) -- 实现思路: - - 从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个顶点进行访问 - - 重复此步骤,直至所有结点都被访问完为止 - -###### DFS 递归写法 -```js -function deepTraversal(node, nodeList) { - if (node) { - nodeList.push(node) - var children = node.children - for (var i = 0; i < children.length; i++) { - deepTraversal(children[i], nodeList) - } - } - return nodeList -} -var root = document.getElementById('root') -console.log(deepTraversal(root,nodeList=[])) -``` - -###### DFS 非递归写法 -```js -function deepTraversal(node) { - var nodeList = [] - if (node) { - var stack = [] - stack.push(node) - while (stack.length != 0) { - var childrenItem = stack.pop() - nodeList.push(childrenItem) - var childrenList = childrenItem.children - for (var i = childrenList.length - 1; i >= 0; i--) { - stack.push(childrenList[i]) - } - } - } - return nodeList -} -var root = document.getElementById('root') -console.log(deepTraversal(root)) -``` - -### 广度优先遍历(BFS) - -- [深度优先遍历(DFS)和广度优先遍历(BFS)](https://www.jianshu.com/p/b4d8085e84bd) -- 实现思路: - - 从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点所有未被访问的邻结点,访问完后再访问这些结点中第一个邻结点的所有结点 - - 重复此方法,直到所有结点都被访问完为止 - -###### BFS 非递归写法 -```js -function wideTraversal(node) { - var nodes = [] - if (node != null) { - var queue = [] - queue.unshift(node) - while (queue.length != 0) { - var item = queue.shift() - nodes.push(item) - var children = item.children - for (var i = 0; i < children.length; i++) { - queue.push(children[i]) - } - } - } - return nodes -} -var root = document.getElementById('root') -console.log(wideTraversal(root)) -``` - - - - -# 未分类试题 - -- [前端和后端的区别](https://zhuanlan.zhihu.com/p/83515211) -- http和http2.0 -- http和https -- 如果要把http升级成https应该怎么做 -- get和post的区别 -- 浏览器缓存机制 -- 浏览器缓存存放位置 -- 浏览器从输入url到页面生成结果哪些步骤 -- tcp三次连接 -- 进程和线程 -- bfc是什么,解决哪些问题 -- ajax的原理和缺点 -- typeof和instanceof的区别 -- 有几种方式判断Array类型 -- 函数深拷贝和浅拷贝的区别,怎么实现深拷贝 -- js垃圾回收机制 -- 事件循环 -- 事件冒泡 -- 阻止事件冒泡的方式,ie是什么方式 -- this的情况 -- js的继承方式,有什么缺点 -- new做了什么事情 -- node线程池 -- node有哪些模块 -- node的事件循环和浏览器的有什么区别 -- node应该怎么读取2DB的数据 -- vue的双向绑定是怎么做到的 -- vue的diff算法是怎么样的 -- vue cil做了哪些事情,你会怎么设计 -- MVC,ajax, 布局,JQuery -- “敏感信息"(密码、身份证之类的)的处理 -- 页面优化相关 -- px rem em -- border-radius,完整写法几个值 -- 实现一个背景色,一半红一半白 -- 什么场景使用HTTP,什么场景使用HTTPS -- web前端学习到了什么程度 -- MVVM和MVC的区别 -- Vue哪些对数组的操作不会导致页面变化 -- websocket原理 -- nocache和nostore的区别 -- 有没有做哪些自适应的页面布局(栅格,圣杯布局) -- cookie和localstorage的区别 -- cookie可以取哪些值 -- xss是什么如何防范 -- csp了解吗 -- async await settimeout promise一堆放在一起的执行结果 -- 隐式类型转换 -- 写个css布局左右定宽中间自适应 -- 数组去掉(false,null, undefine)去重后从大到小排序 -- 给一个对象数组,是一个树的形式存储的城市代码,要求遍历找到id为x的节点输出城市名 -- css怎么实现水平垂直居中 -- css如何处理类名冲突的问题 -- plugin的原理 -- promise怎么实现 -- ip协议有哪些 -- f5随机生成0-5的随机数,如何构造f7 -- 一个桥有20个格子,出发点有无限多的米,一个人每前进一步需要吃十粒米,后退不需要,问如何能够走到桥对面 - - - - -# 祝君无Bug~ - - - - diff --git "a/source/_posts/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223.md" "b/source/_posts/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223.md" deleted file mode 100644 index 71e8ed8ef..000000000 --- "a/source/_posts/2021\345\211\215\347\253\257\351\235\242\350\257\225\346\200\273\347\273\223.md" +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: 2021前端知识点总结 -categories: - - 面试 -tags: - - 面试,Vue,JavaScript -keywords: 面试,Vue,Vue3,JavaScript -description: 2021前端面试总结 -cover: 'https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg.png' -date: 2021-05-24 18:17:08 ---- - - - -# 目录 -[TOC] - - -# JavaScript - -001. **啊** - - 啊 - -001. **啊** - - 啊 - - -# Vue 2.x - -001. **[BiliBili: Vue 2.x 源码解读(12) —— path阶段](https://www.bilibili.com/video/BV1rQ4y1o7WF/?spm_id_from=333.788.recommend_more_video.1)** - - Emm...,慢慢看 - -001. **v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?** - - 源码: `compiler/codegen/index.js` - - 在`Vue 2.x`版本中,`v-if`的优先级大于`v-for` - - 在`Vue 3.x`版本中,`v-for`的优先级大于`v-if` - - `render`函数: `with(this){return _c('div', { ... })}` - -001. **Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?** - - 源码: `src\core\instance\state.js - initData()` - - Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的 - - 采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题 - - 而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况 - -001. **你知道vue中key的作用和工作原理吗?说说你对它的理解。** - - 源码: `src\core\vdom\patch.js - updateChildren()` - - key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两 个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操 作量,提高性能 - - ~~另外,若不设置key还可能在列表更新时引发一些隐蔽的bug(暂时未知)~~ - - vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果 - -001. **你怎么理解vue中的diff算法?** - - 源码1: 必要性,`lifecycle.js - mountComponent()` - - 组件中可能存在很多个data中的key使用 - - 源码2: 执行方式,`patch.js - patchVnode()` - - patchVnode是diff发生的地方,整体策略: 深度优先,同层比较 - - 源码3: 高效性,`patch.js - updateChildren()` - - diff算法是虚拟DOM技术的必然产物: 通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上 - - 另外,也需要 diff 高效的执行对比过程,从而降低时间复杂度为O(n) - - vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方 - - vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch - - diff过程整体遵循`深度优先、同层比较`的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做`首首、尾尾、首尾、尾首`4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。 - -001. **谈一谈对vue组件化的理解?** - - 源码1: 组件定义,`src\core\global-api\assets.js` - - vue-loader会编译template为render函数,最终导出的依然是组件配置对象 - - 源码2: 组件化优点,`lifecycle.js - mountComponent()` - - 组件、Watcher、渲染函数和更新函数之间的关系 - - 源码3: 组件化实现: 构造函数,`src\core\global-api\extend.js`、实例化及挂载,`src\core\vdom\patch.js - createElm()` - - 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用; - - 组件化开发能大幅提高应用开发效率、测试性、复用性等; - - 组件使用按分类有: 页面组件、业务组件、通用组件; - - vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue; - - vue中常见组件化技术有: 属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等; - - 合理的划分组件,有助于提升应用性能; - - 组件应该是高内聚、低耦合的; - - 遵循单向数据流的原则。 - -001. **谈一谈对vue设计原则的理解?** - - 在vue的官网上写着大大的定义和特点: `渐进式JavaScript框架、易用、灵活和高效` - -001. **谈谈你对MVC、MVP和MVVM的理解?** - - 源码: `compiler` - -001. **你了解哪些Vue性能优化方法?** - - 路由懒加载 - - keep-alive缓存页面 - - 使用v-show复用DOM - - v-for 遍历避免同时使用 v-if - - 长列表性能优化,静态列表:`list = Object.freeze([])` - - 虚拟滚动:[vxe-table](https://github.com/x-extends/vxe-table) - - 事件的销毁,Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。`beforeDestroy() { clearInterval(this.timer) }` - - 图片懒加载,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域 内的图片先不做加载,等到滚动到可视区域后再去加载。参考项目:`vue-lazyload`,代码:`` - - 第三方插件按需引入,`import { Button, Select } from 'element-ui';` - - 无状态的组件标记为函数式组件,`` - - 子组件分割,独立可复用功能可抽象出来 - - 变量本地化,如果有for循环等频繁访问`this.xxx`的情况,提前赋值给本地变量 - - SSR - -2. **简单说一说vuex使用及其理解?** - - Vuex实现了一个单向数据流,在全局拥有一个state存放数据,当组件要更改state中的数据时,必须通过mutation提交修改信息,mutation同时提供了订阅者模式供外部插件调用获取state数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作 需要走action,但action也是无法直接修改state的,还是需要通过mutation来修改state的数据。最后,根据state的变化,渲染到视图上。 - -3. **vue中组件之间的通信方式?** - - `props ★★` - - 父组件 A 通过 props 向子组件 B 传递值, B 组件传递 A 组件通过 $emit A 组件通过 v-on/@ 触发 - - 子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。 - - `$emit/$on 事件总线 ★★` - - [vue-bus-ts](https://leedebug.github.io/2020/11/07/%E4%B8%AD%E5%A4%AE%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BA%BF%E6%8F%92%E4%BB%B6vue-bus-ts/) - - `vuex ★★★` - - 结合`localStorage`保存登录信息及权限列表等 - - `$parent/$children` - - `$attrs/$listeners` - - 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法。 - - `$attrs`:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。 - - `$listeners`: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v- on="$listeners" 传入内部组件 - - `provide/inject ★★★` - - 优点:使用简单 缺点:不是响应式 - - 父级:`provide: { name: '王者峡谷' //这种绑定是不可响应的 }`(`name: this`会有响应式,把当前组件实例传递下去,但子组件会绑定一些多余的属性,比如props、methonds等) - - 子级:`inject: ['name'] }` - -4. **vue-router 中的导航钩子由那些?** - - 源码: `compiler` - -5. **什么是递归组件?** - - 源码: `compiler` - -6. **说一说vue响应式理解?** - - 源码: `compiler` - -7. **vue如果想要扩展某个组件现有组件时怎么做?** - - 源码: `compiler` - -8. **vue为什么要求组件模版只能有一个根元素?** - - 源码: `compiler` - -9. **watch和computed的区别以及怎么选用?** - - 源码: `compiler` - -10. **你知道nextTick的原理吗?** - - 源码: `compiler` - -11. **你知道vue双向数据绑定的原理吗?** - - 源码: `compiler` - -12. **简单说一说vue生命周期的理解?** - - 源码: `compiler` - - -# Vue 3.x - -001. **[vue3.x的新特性研究](https://blog.csdn.net/sky_cmc/article/details/104988921)** - - Emm...,慢慢看 - - -# Webpack - -001. **啊** - - 啊 - - -# Css & Css3 - -001. **啊** - - 啊 - - -# 项目结构化? - -001. **啊** - - 啊 - - -# 祝君无Bug~ \ No newline at end of file diff --git a/source/_posts/GitHub-Corners.md b/source/_posts/GitHub-Corners.md deleted file mode 100644 index 11d63b6a5..000000000 --- a/source/_posts/GitHub-Corners.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: GitHub Corners -categories: - - Git -tags: - - Git -keywords: Git -description: GitHub Corners -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201215232213.png' -date: 2020-12-15 23:19:34 ---- - -> 可以在你的博客或者自己的网站上增加一个github的标志链接 - -# GitHub Ribbons - -[github repo](https://github.blog/2008-12-19-github-ribbons/)是最初的角标链接,像丝带一样的长条形状,如下图所示: - -![GitHub Ribbons](https://github.blog/wp-content/uploads/2008/12/forkme_right_darkblue_121621.png?resize=149%2C149) - -接入代码如下所示: - -```html -Fork me on GitHub -``` - -# GitHub Corners - -[GitHub Corners](https://tholman.com/github-corners/)是一款更为简单干净的角标链接,如本站右上角所示: - -接入代码如下所示: - -```html - - -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247.md" "b/source/_posts/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247.md" deleted file mode 100644 index 54efb92c8..000000000 --- "a/source/_posts/JavaScript\350\277\220\347\256\227\347\254\246\344\274\230\345\205\210\347\272\247.md" +++ /dev/null @@ -1,341 +0,0 @@ ---- -title: JavaScript运算符优先级 -categories: - - JavaScript -tags: - - JavaScript -keywords: JavaScript -description: JavaScript运算符优先级 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210422112132.png' -date: 2021-04-08 10:11:48 ---- - -> 运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。 - -# 优先级汇总表 - -下面的表将所有运算符按照优先级的不同从高(20+)到低(1)排列。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    优先级运算类型关联性运算符
    21圆括号n/a(不相关)( … )
    20成员访问从左到右… . …
    需计算的成员访问从左到右… [ … ]
    new (带参数列表)n/anew … ( … )
    函数调用从左到右… ( … )
    可选链(Optional chaining)从左到右?.
    19new (无参数列表)从右到左new …
    18后置递增(运算符在后)n/a
    -  
    … ++
    后置递减(运算符在后)… --
    17逻辑非从右到左! …
    按位非~ …
    一元加法+ …
    一元减法- …
    前置递增++ …
    前置递减-- …
    typeoftypeof …
    voidvoid …
    deletedelete …
    awaitawait …
    16从右到左… ** …
    15乘法从左到右
    -  
    … * …
    除法… / …
    取模… % …
    14加法从左到右
    -  
    … + …
    减法… - …
    13按位左移从左到右… << …
    按位右移… >> …
    无符号右移… >>> …
    12小于从左到右… < …
    小于等于… <= …
    大于… > …
    大于等于… >= …
    in… in …
    instanceof… instanceof …
    11等号从左到右
    -  
    … == …
    非等号… != …
    全等号… === …
    非全等号… !== …
    10按位与从左到右… & …
    9按位异或从左到右… ^ …
    8按位或从左到右… | …
    7逻辑与从左到右… && …
    6逻辑或从左到右… || …
    5空值合并从左到右… ?? …
    4条件运算符从右到左… ? … : …
    3赋值从右到左… = …
    … += …
    … -= …
    … **= …
    … *= …
    … /= …
    … %= …
    … <<= …
    … >>= …
    … >>>= …
    … &= …
    … ^= …
    … |= …
    … &&= …
    … ||= …
    … ??= …
    2yield从右到左yield …
    yield*yield* …
    1展开运算符n/a... …
    0逗号从左到右… , …
    - -# 示例 - -```js -A && B -// 若A为true,则return B。若A为false,return A - -A || B -// 若A为true,return A。若A为false,则return B - -false || 3 && 2 -// ||的左侧为false,所以返回(3 && 2)的结果; -// 又,3为true,所以直接返回2 -``` - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/LeetCode\345\270\270\350\247\201\351\242\230.md" "b/source/_posts/LeetCode\345\270\270\350\247\201\351\242\230.md" deleted file mode 100644 index f212ac084..000000000 --- "a/source/_posts/LeetCode\345\270\270\350\247\201\351\242\230.md" +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: LeetCode常见题 -categories: - - LeetCode -tags: - - LeetCode -keywords: 'LeetCode' -description: LeetCode -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170207.png -date: 2020-07-01 22:32:05 ---- - -> 算法对前端的重要性可以说至关重要,不可小觑。 - -# 算法思想 - -- 基础技巧: 分治、二分、贪心 -- 排序算法: 快速排序、归并排序、计数排序 -- 搜索算法: 回溯、递归、深度优先遍历,广度优先遍历,二叉搜索树等 -- 图论: 最短路径、最小生成树 -- 动态规划: 背包问题、最长子序列 - -## - 双指针 - -> 88. 合并两个有序数组 - -给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 - -说明: -初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 -你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 - -示例: -- 输入: - - nums1 = [1,2,3,0,0,0], m = 3 - - nums2 = [2,5,6], n = 3 -- 输出: - - [1,2,2,3,5,6] - -```js -/** - * @param {number[]} nums1 - * @param {number} m - * @param {number[]} nums2 - * @param {number} n - * @return {void} Do not return anything, modify nums1 in-place instead. - */ -var merge = function(nums1, m, nums2, n) { - var i = m - 1, j = n - 1, len = m + n -1 - while (i >= 0 || j >= 0) { - if (i < 0) { - nums1[len--] = nums2[j--] - } else if (j < 0) { - nums1[len--] = nums1[i--] - } else if (nums1[i] > nums2[j]) { - nums1[len--] = nums1[i--] - } else { - nums1[len--] = nums2[j--] - } - } -}; -``` - -## - 排序 - -## - 贪心思想 - -## - 二分查找 - -## - 分治 - -## - 搜索 - -## - 动态规划 - -## - 数学 - - -# 数据结构相关 - -- 数组与链表:单/双向链表 -- 栈与队列 -- 哈希表 -- 堆:最大堆/最小堆 -- 树与图:最近公共祖先、并查集 -- 字符串:前缀树(字典树) /后缀树 - -## - 链表 - -## - 树 - -## - 栈和队列 - -## - 哈希表 - -## - 字符串 - -## - 数组与矩阵 - -## - 图 - -## - 位运算 - - -# 祝君无Bug~ diff --git "a/source/_posts/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key.md" "b/source/_posts/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key.md" deleted file mode 100644 index 1adeaca78..000000000 --- "a/source/_posts/Mac\351\205\215\347\275\256\345\244\232\344\270\252SSH-Key.md" +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Mac配置多个SSH-Key -categories: - - Git -tags: - - Git -keywords: Git -description: Mac配置多个SSH-Key -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201211231351.png' -date: 2020-12-11 23:11:41 ---- - -> 随着项目与能力的提升与扩展,一台电脑上同时用着多个git仓库的情况越来越普遍,所以我们需要创建多个ssh key来对应不同的账号。本文以github为例 - -# 本地配置ssh秘钥和公钥 - -## 进入到ssh文件夹下 - -```bash -→ cd ~/.ssh/ -``` - -## 生成一个ssh-key - -引号内填写你github对应的邮箱 - -```bash -→ ssh-keygen -t rsa -b 4096 -C "your email" -``` - -如果你之前mac上创建过ssh-key,在`.ssh`文件夹下会有`id_rsa`和`id_rsa.pub`两个文件,分别为秘钥和公钥;当再次创建时,会有如下提示: - -```bash -Generating public/private rsa key pair. -Enter file in which to save the key (/Users/xxx/.ssh/id_rsa): -``` - -如果想覆盖之前的文件直接回车即可;如果想创建新的ssh-key,则需要在此处输入新的名称,如`id_rsa_github`,回车后会提示你下次使用此ssh-key时是否需要密码,如果是个人电脑,直接回车即可(如果需要,设置便可) - -```bash -Enter passphrase (empty for no passphrase): -Enter same passphrase again: -``` - -设置完密码后,会提示你ssh-key设置成功 - -```bash -our identification has been saved in id_rsa_github. -Your public key has been saved in id_rsa_github.pub. -The key fingerprint is: -SHA256: hash值 你的邮箱 -The key's randomart image is: -+---[RSA 4096]----+ -| .@/0+ o | -| o=+0*O o | -| .o=.*** .| -| o.o B = | -| @ + + * .| -| o + + o . | -| o . o | -| | -| | -+----[SHA256]-----+ -``` - -查看`.ssh`文件夹下的新文件,则会看到刚才新生成的`id_rsa_github`秘钥和`id_rsa_github.pub`公钥 - -```bash -→ ls -id_rsa -id_rsa.pub -id_rsa_github -id_rsa_github.pub -``` - -## 将ssh-key添加到ssh-agent - -因为本地默认只读`id_rsa`,我们想要使用新的秘钥对则需要把新添加的ssh-key添加到ssh-agent - -首先,查看ssh agent所有密钥对(如果有以下提示,则表示从未添加过) - -```bash -→ ssh-add -l -The agent has no identities. -``` - -将新的ssh-key添加到ssh agent - -```bash -→ ssh-add id_rsa_github -Identity added: id_rsa_github (id_rsa_github) -``` - -此时再次查看ssh agent - -```bash -→ ssh-add -l -4096 SHA256: hash值 id_rsa_github (RSA) -``` - -# 配置github的ssh-key - -首先查看我们刚配置好的ssh-key的公钥,即以`*.pub`为后缀名的文件。复制整个文件的内容,即公钥信息 - -```bash -→ cat id_rsa_github.pub -ssh-rsa 公钥 你的邮箱 -``` - -登录github后,在右上角的头像下拉列表中选择`Settings`选项,在左侧菜单中选择`SSH and GPG keys`,即可看到当前的SSH Keys,点击右上角绿色的`New SSH Key`按钮,在`Key`下方的文本框中,粘贴你的公钥信息,会默认以你的邮箱作为`title`(也可自行更改),点击下方的`Add SSH Key`即可。步骤如下图所示 - -![github的ssh-key](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201211234542.png) - -# 测试连接 - -回到自己的项目仓库下`git fetch`;或打开控制台,输入以下命令 - -```bash -→ ssh -T git@github.com -Hi LeeDebug! You've successfully authenticated, but GitHub does not provide shell access. -``` - -到此,你的ssh-key已经可以正常使用,你可以使用ssh的方式去clone项目了 - - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244.md" "b/source/_posts/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244.md" deleted file mode 100644 index 2f00f5703..000000000 --- "a/source/_posts/Ubuntu-16-04-\351\203\250\347\275\262\345\221\275\344\273\244.md" +++ /dev/null @@ -1,339 +0,0 @@ ---- -title: Ubuntu 16.04 部署命令 -categories: - - 服务器 -tags: - - 服务器 -keywords: '服务器' -description: 服务器 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170149.png -date: 2020-07-06 15:17:53 ---- - -> 现时代身为一名前端工程师,配置服务器、部署线上项目等技能是必不可少的。 - -# 参考文档 -- [使用 pyenv 可以在一个系统中安装多个python版本](https://www.jianshu.com/p/a23448208d9a) - - -# 配置步骤 - -## 基本配置 - -```bash -# 连接 -ssh -p 22 root@47.104.223.131 - -# 修改主机名 -sudo vi /etc/hostname -改为: - LeeJs - -# 修改配置文件 -sudo vi /etc/hosts -添加: - 127.0.1.1 LeeJs - -# 重启 -reboot - -# apt 检测更新 -sudo apt update -apt list --upgradable - -# 查看版本信息 -lsb_release -a -uname -a -cat /proc/version -``` - ---- -** ⚠️ 注:以下命令已打包至 install.sh ** - - -## 安装yum(不需要) -```bash -apt install yum -``` - - -## 安装 ruby(不需要) -```bash -apt install ruby -``` - - -## 安装 LinuxBrew(不需要) -```bash -sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" -``` - - -## 安装 screen -```bash -apt install screen - -# 查看路径 -which screen - -# 查看版本 -screen -v - -# 查看所有 -screen -ls - -# 新建 -screen -S qf_backend - -# 恢复之前在线的screen,并置位离线 -screen -x -d qf_backend/PID - -# 重连 -screen -r qf_backend/PID -ctrl + r ==> 输入PID - -# 先重连,若找不到离线作业,则新建 -screen -R qf_backend/PID - -# 删除screen -screen -X -S PID quit -``` - - -## 安装 git -```bash -apt install git -``` - - -## 安装 pyenv -```bash -sudo curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash - -# 添加源 -sudo vi ~/.bashrc - -# 添加下面三行 -export PATH="/root/.pyenv/bin:$PATH" -eval "$(pyenv init -)" -eval "$(pyenv virtualenv-init -)" - -# 更新源 -source ~/.bashrc -``` - - -## 安装「 python 所需依赖包 」 -```bash -apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline6 libreadline6-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev zlib* -``` - - -- ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib? -```bash -apt install libbz2-dev libssl-dev libsqlite3-dev libreadline6 libreadline6-dev -``` - - -## 通过 pyenv 安装 python -```bash -# 下载 python -wget http://mirrors.sohu.com/python/3.6.5/Python-3.6.5.tar.xz -P ~/.pyenv/cache/ - -# 查看目录 -cd ~/.pyenv/cache/ - -# 所有支持版本 -pyenv install --list - -# 安装指定版本python,-v:显示全过程 -pyenv install 3.6.5 -v - -# 刷新pyenv -pyenv rehash - -# 查看所有的版本 -pyenv versions - -# 安装pyenv-virtualenv插件 -git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv - -# 以python 3.6.5 新建名为 qf_admin_3.6.5 的虚拟环境 -pyenv virtualenv 3.6.5 qf_admin_3.6.5 - -# 激活虚拟环境 -pyenv activate qf_admin_3.6.5 - -# 离开已经激活的环境 -pyenv deactivate - -# 设置当前路径的python版本,优先级:shell > local > global -pyenv local 3.6.5 - -# 删除虚拟环境(删除所在目录即可) -rm -rf ~/.pyenv/versions/test3.7.3/ - -``` - - -## 安装 mysql -- [如何在Ubuntu 16.04下安装MySQL](https://blog.csdn.net/james_nan/article/details/82053430) -```bash -# 安装 mysql 基础服务 -apt install mysql-server -apt install mysql-client -apt install libmysqlclient-dev - -# 测试是否安装成功 -netstat -tap | grep mysql - -# 打开数据库 -mysql -uroot -p - -# 配置文件 -sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf - -# 重启 mysql 服务 -service mysql restart -``` - - -## 安装 nginx -```bash -# 安装 nginx -apt install nginx - -# 查看nginx安装路径 -whereis nginx - -# 查看 nginx 加载的哪个配置文件 -sudo nginx -t - -# 配置文件位置 -cat /etc/nginx/nginx.conf - -# 启动 -nginx - -# 重启 -nginx -s reload -``` - - -## 配置 HTTPS 443 - -- [Django 中将HTTP转换为HTTPS [已成功???]](https://blog.csdn.net/qq1154479896/article/details/89408428) -- [在本地环境(mac)启用https](https://www.cnblogs.com/programs/p/11043169.html) -- [阿里云服务器https配置](https://blog.csdn.net/wqhjfree/article/details/77256131) - -```bash -# 服务器报错 400 -You're accessing the development server over HTTPS, but it only supports HTTP. - -# 安装 pyOpenSSL(测试时用) -pip install pyOpenSSL - -# 以 https 方式启动 django(测试专用) -python manage.py runserver_plus --cert cert.crt 0.0.0.0:23480 - --------------------------------- - -# SSL 证书 -阿里云:https://yundunnext.console.aliyun.com/?spm=5176.2020520163.aliyun_sidebar.daliyun_sidebar_cas.5f3956a7FAnrMY&p=cas#/overview/cn-hangzhou -下载并解压后放在:/etc/nginx/certificate/ - -# nginx https 配置 -server - { - listen 443; - server_name www.lee521.top; - ssl on; - root html; - index index.html index.htm; - ssl_certificate /etc/nginx/certificate/2501940_lee521.top.pem; - ssl_certificate_key /etc/nginx/certificate/2501940_lee521.top.key; - ssl_session_timeout 5m; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - location / { - root html; - index index.html index.htm; - } - location ~ /api { - proxy_pass http://127.0.0.1:23480; - } - location ~ /doc { - proxy_pass http://127.0.0.1:23480; - } - location ~ /static { - proxy_pass http://127.0.0.1:23480; - } - } - -# 配置成功 ! -https://www.lee521.top/doc/ -https://www.lee521.top/api/system/system_user/ -``` - - -## 配置前端 -```bash - server { - listen 10086; - server_name localhost; - root /root/www/vue-element-admin/dist/; - } -``` - - -# 打包 install.sh 脚本文件 - -**==执行 source ~/.bashrc 会出错,提示 source not found,可以分两批执行==** - -```bash -sudo apt update -apt list --upgradable -# need keyboard input: y -sudo apt install yum -# need keyboard input: y -sudo apt install ruby -sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" -# need keyboard input: y -sudo apt install screen -sudo apt install git -# install pyenv -rm -rf ~/.pyenv/ -sudo curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash - -# update source -sed -i '1i export PATH="/root/.pyenv/bin:$PATH"' ~/.bashrc -sed -i '2i eval "$(pyenv init -)"' ~/.bashrc -sed -i '3i eval "$(pyenv virtualenv-init -)"' ~/.bashrc -# 执行这一句会出错,提示 source not found -source ~/.bashrc - -# install depend package -sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev -# download python 3.6.5 -wget http://mirrors.sohu.com/python/3.6.5/Python-3.6.5.tar.xz -P ~/.pyenv/cache/ -# install 2.6.5 with pyenv -pyenv install 3.6.5 -v -# refresh pyenv -pyenv rehash -# clone pyenv-virtualenv -git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv -# new a virtualenv named custom_3.6.5 with python3.6.5 -pyenv virtualenv 3.6.5 custom_3.6.5 -# show all virtualenv -pyenv versions -``` - -# 新版项目启动代码 - -```bash -gunicorn -c ./gunicorn-config.py backend.wsgi -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments.md" "b/source/_posts/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments.md" deleted file mode 100644 index 56d5f1788..000000000 --- "a/source/_posts/VSCode\346\263\250\351\207\212\351\253\230\344\272\256\346\217\222\344\273\266BetterComments.md" +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: VSCode注释高亮插件 Better Comments -categories: - - VSCode -tags: - - VSCode -keywords: VSCode,Better Comments -description: VSCode注释高亮插件 Better Comments -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210422112146.png' -date: 2021-03-25 12:21:54 ---- - -> 本文概要 - -# 安装插件 - -打开VSCode的插件市场,搜索`Better Comments`,点击`install` - -![插件安装](https://img2020.cnblogs.com/blog/1725797/202007/1725797-20200707222847183-973879171.png) - -# 使用 - -该插件提供5中默认的高亮方式,配置信息如下: - -```js -"better-comments.tags": [ - { - "tag": "!", - "color": "#FF2D00", - "strikethrough": false, - "underline": false, - "backgroundColor": "transparent", - "bold": false, - "italic": false - }, - { - "tag": "?", - "color": "#3498DB", - "strikethrough": false, - "underline": false, - "backgroundColor": "transparent", - "bold": false, - "italic": false - }, - { - "tag": "//", - "color": "#474747", - "strikethrough": true, - "underline": false, - "backgroundColor": "transparent", - "bold": false, - "italic": false - }, - { - "tag": "todo", - "color": "#FF8C00", - "strikethrough": false, - "underline": false, - "backgroundColor": "transparent", - "bold": false, - "italic": false - }, - { - "tag": "*", - "color": "#98C379", - "strikethrough": false, - "underline": false, - "backgroundColor": "transparent", - "bold": false, - "italic": false - } -] -``` - -效果如下所示: - -![](https://img2020.cnblogs.com/blog/1725797/202007/1725797-20200707222904071-401665639.png) - -![](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210421182451.jpg) - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel.md" "b/source/_posts/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel.md" deleted file mode 100644 index 5efb70e53..000000000 --- "a/source/_posts/Vue3\350\275\256\346\222\255\347\273\204\344\273\266-v3-carousel.md" +++ /dev/null @@ -1,188 +0,0 @@ ---- -title: '【个人开源】Vue3 轮播组件: v3-carousel' -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 'Vue3 轮播组件: v3-carousel' -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210527141809.png' -date: 2021-05-27 12:55:02 -sticky: 99 ---- - -> 引水方知开源不易 -> -> 与朋友首次开源了一个轮播插件,希望大家积极品尝 - - -# 介绍 - -基于 vue3 composition api 编写的轮播插件,多种属性适配,轮播内容 `可完全自定义`,基本可以满足大部分的轮播需求。 - -基本功能介绍: -- 是否开启自动轮播,自定义轮播时间 -- 鼠标移入后暂停轮播,鼠标移出后重置轮播 -- 点击 左侧/右侧 切换按钮,手动切换 -- 点击 底部轮播指示器,手动切换 -- 切换按钮 与 轮播指示器,可设置 hover 展示 -- 左侧切换向左滚动,右侧切换向右滚动 -- ... - -在线Demo:正在制作... - - -# 安装 - -```bash -npm install v3-carousel -``` -or -```bash -yarn add v3-carousel -``` -# 使用 - -`main.js` - -```js -import { createApp } from "vue"; -import App from "./App.vue"; -import Carousel from "v3-carousel"; // 引入 - -const app = createApp(App) -app.use(Carousel).mount('#app') // 使用 -``` - -> 注意点:将你需要显示的图片使用`CarouselItem`包裹起来(创建`CarouselItem`暂时必须使用`v-for`循环完成,因为我需要使用到`idx`来操作),完成之后你还需要将这些`CarouselItem`用一个大的`Carousel`包裹起来,再给`Carousel`添加你需要的属性,好了,到这里一个实例就完成了,你可以去网页上查看轮播图了 - -`App.vue` - -```html - - - -``` - - -# 参数 -## Carousel 组件选项(Props) - -| property name | type | default value | meaning | -| -------------------- | ------- | ------------- | ------- | -| containerWidth | String | 100% | 设置整个carousel容器的宽度,当然你也可以使用vw,rem,em等像素单位 | -| containerHeight | String | 100% | 设置整个carousel容器的g高度,同上 | -| duration | Number | 3000 | 轮播间隔是多久一次 | -| initIndex | Number | 0 | 初始化显示的图片索引 | -| autoplay | Boolean | true | 是否自动开始轮播 | -| direction | Boolean | true | 是否需要切换按钮(即 上、下一张按钮) | -| directionMode | String | always | 切换按钮的展示方式,可选 always、hover | -| directionColor | String | white | 切换按钮的颜色 | -| directionSize | Number | 25 | 切换按钮的大小,单位(px) | -| indicator | Boolean | true | 是否需要轮播图指示器(底部当前选中标识) | -| indicatorMode | String | always | 切换按钮的展示方式,可选 always、hover | -| indicatorColor | String | #FFFFFF80 | 未选中时的指示器颜色 | -| indicatorActiveColor | String | #FFFFFF | 选中时的指示器颜色 | - - - -## Carousel 事件(Event) - -| event Name | parmas | meaning | -| -------------- | ------ | ------- | -| @before-moving | 该钩子函数拥有一个对象参数,你可以获取到它们:轮播的方向(**direction**)以及当前轮播的索引(**index**) | 视图移动前会执行的钩子函数,如果您想在轮播图轮播前做一些逻辑可以使用该钩子 | -| @after-moving | 同上... | 视图移动完成后会执行的钩子函数,如果您想在轮播图轮播完成之后做一些逻辑可以使用该钩子 | - -## CarouselItem 组件选项(Props) -| property name | type | default value | meaning | -| ------------- | ------- | ------------- | ------- | -| idx | Number | 0 | 每个子实例对应的索引,一般用 `v-for` 中的第二个参数即可 | - - - -# 联系方式 - -> 使用如果出现问题欢迎来讨论,觉得好用的话就点个 `star` 吧,o(* ̄▽ ̄*)o -> -> 有什么建议欢迎大佬们提交 `pr`,谢谢! - -### repo归属者 -- WeChat: x972761675 -- 前端qq交流群: 700785102 - -### 目前维护者: 淳淳同学 -- [个人博客](https://leedebug.github.io/) -- [Github](https://github.com/LeeDebug) -- [掘金](https://juejin.cn/user/2189882894323975/posts) -- [语雀](https://www.yuque.com/LeeDebug) -- [简书](https://www.jianshu.com/u/fc47eb26e53c) -- [开源中国](https://my.oschina.net/LeeDebug) -- [博客园](https://www.cnblogs.com/LeeDebug/) -- 微信二维码: - - - - -
    - WeChat:lcc961150665 -
    - - - -# 相关链接 - -- [npm package](https://www.npmjs.com/package/v3-carousel) -- [github repo](https://github.com/Acmenlei/v3-carousel) - - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211.md" "b/source/_posts/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211.md" deleted file mode 100644 index 7b47cbbd9..000000000 --- "a/source/_posts/Vue\346\272\220\347\240\201\350\247\243\350\257\273\357\274\210\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223\357\274\211.md" +++ /dev/null @@ -1,616 +0,0 @@ ---- -title: Vue源码解读(知识点总结) -categories: - - Vue -tags: - - Vue -keywords: Vue -description: Vue源码解读(知识点总结) -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210706123716.png' -date: 2021-07-05 23:55:11 ---- - -> 为了方便自己对知识点的巩固和理解,整理了李永宁大佬 12 篇《Vue源码解读》的文末知识点总结,在这里可以一览天下。如果想看详细文章,可点击标题下方的“阅读原文”即可。 - -# (1)前言 - -[阅读原文](https://juejin.cn/post/6949370458793836580) - - -# (2)Vue 初始化过程 - -[阅读原文](https://juejin.cn/post/6950084496515399717) - -### Vue 的初始化过程(new Vue(options))都做了什么? - -- 处理组件配置项 - - 初始化根组件时进行了选项合并操作,将全局配置合并到根组件的局部配置上 - - 初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以提高代码的执行效率 -- 初始化组件实例的关系属性,比如 $parent、$children、$root、$refs 等 -- 处理自定义事件 -- 调用 beforeCreate 钩子函数 -- 初始化组件的 inject 配置项,得到 ret[key] = val 形式的配置对象,然后对该配置对象进行响应式处理,并代理每个 key 到 vm 实例上 -- 数据响应式,处理 props、methods、data、computed、watch 等选项 -- 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上 -- 调用 created 钩子函数 -- 如果发现配置项上有 el 选项,则自动调用 `$mount` 方法,也就是说有了 el 选项,就不需要再手动调用 `$mount` 方法,反之,没提供 el 选项则必须调用 `$mount` -- 接下来则进入挂载阶段 - - - -# (3)响应式原理 - -[阅读原文](https://juejin.cn/post/6950826293923414047) - -### Vue 响应式原理是怎么实现的? - -- 响应式的核心是通过 Object.defineProperty 拦截对数据的访问和设置 -- 响应式的数据分为两类: - - 对象,循环遍历对象的所有属性,为每个属性设置 getter、setter,以达到拦截访问和设置的目的,如果属性值依旧为对象,则递归为属性值上的每个 key 设置 getter、setter - - 访问数据时(obj.key)进行依赖收集,在 dep 中存储相关的 watcher - - 设置数据时由 dep 通知相关的 watcher 去更新 - - 数组,增强数组的那 7 个可以更改自身的原型方法,然后拦截对这些方法的操作 - - 添加新数据时进行响应式处理,然后由 dep 通知 watcher 去更新 - - 删除数据时,也要由 dep 通知 watcher 去更新 - -### methods、computed 和 watch 有什么区别? - -```html - - - - - methods、computed、watch 有什么区别 - - - -
    - -
    {{ returnMsg() }}
    -
    {{ returnMsg() }}
    - -
    {{ getMsg }}
    -
    {{ getMsg }}
    -
    - - - - - -``` - -![methodsComputedWatch.gif](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9c957654bb484ae7ba4ace1b912cff03~tplv-k3u1fbpfcp-watermark.image) - -示例其实就是答案了 -- 使用场景 - - methods 一般用于封装一些较为复杂的处理逻辑(同步、异步) - - computed 一般用于封装一些简单的同步逻辑,将经过处理的数据返回,然后显示在模版中,以减轻模版的重量 - - watch 一般用于当需要在数据变化时执行异步或开销较大的操作 -- 区别 - - methods VS computed - - 通过示例会发现,如果在一次渲染中,有多个地方使用了同一个 methods 或 computed 属性,methods 会被执行多次,而 computed 的回调函数则只会被执行一次。 - - 通过阅读源码我们知道,在一次渲染中,多次访问 computedProperty,只会在第一次执行 computed 属性的回调函数,后续的其它访问,则直接使用第一次的执行结果(watcher.value),而这一切的实现原理则是通过对 watcher.dirty 属性的控制实现的。而 methods,每一次的访问则是简单的方法调用(this.xxMethods)。 - - computed VS watch - - 通过阅读源码我们知道,computed 和 watch 的本质是一样的,内部都是通过 Watcher 来实现的,其实没什么区别,非要说区别的化就两点:1、使用场景上的区别,2、computed 默认是懒执行的,切不可更改。 - - methods VS watch - - methods 和 watch 之间其实没什么可比的,完全是两个东西,不过在使用上可以把 watch 中一些逻辑抽到 methods 中,提高代码的可读性。 - - - -# (4)异步更新 - -[阅读原文](https://juejin.cn/post/6951568091893465102) - -### Vue 的异步更新机制是如何实现的? - -- Vue 的异步更新机制的核心是利用了浏览器的异步任务队列来实现的,首选微任务队列,宏任务队列次之。 -- 当响应式数据更新后,会调用 dep.notify 方法,通知 dep 中收集的 watcher 去执行 update 方法,watcher.update 将 watcher 自己放入一个 watcher 队列(全局的 queue 数组)。 -- 然后通过 nextTick 方法将一个刷新 watcher 队列的方法(flushSchedulerQueue)放入一个全局的 callbacks 数组中。 -- 如果此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数,则执行 timerFunc 函数,将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数,等待其执行完成以后再放入下一个 flushCallbacks 函数。 -- flushCallbacks 函数负责执行 callbacks 数组中的所有 flushSchedulerQueue 函数。 -- flushSchedulerQueue 函数负责刷新 watcher 队列,即执行 queue 数组中每一个 watcher 的 run 方法,从而进入更新阶段,比如执行组件更新函数或者执行用户 watch 的回调函数。 -- 完整的执行过程其实就是今天源码阅读的过程。 - -### Vue 的 nextTick API 是如何实现的? - -Vue.nextTick 或者 vm.$nextTick 的原理其实很简单,就做了两件事: -- 将传递的回调函数用 try catch 包裹然后放入 callbacks 数组 -- 执行 timerFunc 函数,在浏览器的异步任务队列放入一个刷新 callbacks 数组的函数 - - - -# (5)全局 API - -[阅读原文](https://juejin.cn/post/6952643167715852319) - -### Vue.use(plugin) 做了什么? - -负责安装 plugin 插件,其实就是执行插件提供的 install 方法。 -- 首先判断该插件是否已经安装过 -- 如果没有,则执行插件提供的 install 方法安装插件,具体做什么有插件自己决定 - -### Vue.mixin(options) 做了什么? - -负责在 Vue 的全局配置上合并 options 配置。然后在每个组件生成 vnode 时会将全局配置合并到组件自身的配置上来。 -- 标准化 options 对象上的 props、inject、directive 选项的格式 -- 处理 options 上的 extends 和 mixins,分别将他们合并到全局配置上 -- 然后将 options 配置和全局配置进行合并,选项冲突时 options 配置会覆盖全局配置 - -### Vue.component(compName, Comp) 做了什么? - -负责注册全局组件。其实就是将组件配置注册到全局配置的 components 选项上(options.components),然后各个子组件在生成 vnode 时会将全局的 components 选项合并到局部的 components 配置项上。 -- 如果第二个参数为空,则表示获取 compName 的组件构造函数 -- 如果 Comp 是组件配置对象,则使用 Vue.extend 方法得到组件构造函数,否则直接进行下一步 -- 在全局配置上设置组件信息,this.options.components.compName = CompConstructor - -### Vue.directive('my-directive', {xx}) 做了什么? - -在全局注册 my-directive 指令,然后每个子组件在生成 vnode 时会将全局的 directives 选项合并到局部的 directives 选项中。原理同 Vue.component 方法: -- 如果第二个参数为空,则获取指定指令的配置对象 -- 如果不为空,如果第二个参数是一个函数的话,则生成配置对象 { bind: 第二个参数, update: 第二个参数 } -- 然后将指令配置对象设置到全局配置上,this.options.directives['my-directive'] = {xx} - -### Vue.filter('my-filter', function(val) {xx}) 做了什么? - -负责在全局注册过滤器 my-filter,然后每个子组件在生成 vnode 时会将全局的 filters 选项合并到局部的 filters 选项中。原理是: -- 如果没有提供第二个参数,则获取 my-filter 过滤器的回调函数 -- 如果提供了第二个参数,则是设置 this.options.filters['my-filter'] = function(val) {xx}。 - -### Vue.extend(options) 做了什么? - -Vue.extend 基于 Vue 创建一个子类,参数 options 会作为该子类的默认全局配置,就像 Vue 的默认全局配置一样。所以通过 Vue.extend 扩展一个子类,一大用处就是内置一些公共配置,供子类的子类使用。 -- 定义子类构造函数,这里和 Vue 一样,也是调用 _init(options) -- 合并 Vue 的配置和 options,如果选项冲突,则 options 的选项会覆盖 Vue 的配置项 -- 给子类定义全局 API,值为 Vue 的全局 API,比如 Sub.extend = Super.extend,这样子类同样可以扩展出其它子类 -- 返回子类 Sub - -### Vue.set(target, key, val) 做了什么? - -由于 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi'),所以通过 Vue.set 为向响应式对象中添加一个 property,可以确保这个新 property 同样是响应式的,且触发视图更新。 -- 更新数组指定下标的元素:Vue.set(array, idx, val),内部通过 splice 方法实现响应式更新 -- 更新对象已有属性:Vue.set(obj, key ,val),直接更新即可 => obj[key] = val -- 不能向 Vue 实例或者 $data 动态添加根级别的响应式数据 -- Vue.set(obj, key, val),如果 obj 不是响应式对象,会执行 obj[key] = val,但是不会做响应式处理 -- Vue.set(obj, key, val),为响应式对象 obj 增加一个新的 key,则通过 defineReactive 方法设置响应式,并触发依赖更新 - -### 面试官 问:Vue.delete(target, key) 做了什么? - -删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,但是你应该很少会使用它。当然同样不能删除根级别的响应式属性。 -- Vue.delete(array, idx),删除指定下标的元素,内部是通过 splice 方法实现的 -- 删除响应式对象上的某个属性:Vue.delete(obj, key),内部是执行 delete obj.key,然后执行依赖更新即可 - -### Vue.nextTick(cb) 做了什么? - -Vue.nextTick(cb) 方法的作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想立即获取更改过后的 DOM 数据: - -```javascript -this.key = 'new val' - -Vue.nextTick(function() { - // DOM 更新了 -}) -``` - -其内部的执行过程是: -- this.key = 'new val,触发依赖通知更新,将负责更新的 watcher 放入 watcher 队列 -- 将刷新 watcher 队列的函数放到 callbacks 数组中 -- 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数 -- Vue.nextTick(cb) 来插队,将 cb 函数放入 callbacks 数组 -- 待将来的某个时刻执行刷新 callbacks 数组的函数 -- 然后执行 callbacks 数组中的众多函数,触发 watcher.run 的执行,更新 DOM -- 由于 cb 函数是在后面放到 callbacks 数组,所以这就保证了先完成的 DOM 更新,再执行 cb 函数 - - - -# (6)实例方法 - -[阅读原文](https://juejin.cn/post/6953503236254859294) - -### 面试官 问:vm.$set(obj, key, val) 做了什么? - -`vm.$set` 用于向响应式对象添加一个新的 property,并确保这个新的 property 同样是响应式的,并触发视图更新。由于 Vue 无法探测对象新增属性或者通过索引为数组新增一个元素,比如:`this.obj.newProperty = 'val'`、`this.arr[3] = 'val'`。所以这才有了 `vm.$set`,它是 `Vue.set` 的别名。 -- 为对象添加一个新的响应式数据:调用 defineReactive 方法为对象增加响应式数据,然后执行 dep.notify 进行依赖通知,更新视图 -- 为数组添加一个新的响应式数据:通过 splice 方法实现 - -### vm.$delete(obj, key) 做了什么? - -vm.$delete 用于删除对象上的属性。如果对象是响应式的,且能确保能触发视图更新。该方法主要用于避开 Vue 不能检测属性被删除的情况。它是 Vue.delete 的别名。 -- 删除数组指定下标的元素,内部通过 splice 方法来完成 -- 删除对象上的指定属性,则是先通过 delete 运算符删除该属性,然后执行 dep.notify 进行依赖通知,更新视图 - -### vm.$watch(expOrFn, callback, [options]) 做了什么? - -vm.$watch 负责观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。当其发生变化时,回调函数就会被执行,并为回调函数传递两个参数,第一个为更新后的新值,第二个为老值。 - -这里需要 注意 一点的是:如果观察的是一个对象,比如:数组,当你用数组方法,比如 push 为数组新增一个元素时,回调函数被触发时传递的新值和老值相同,因为它们指向同一个引用,所以在观察一个对象并且在回调函数中有新老值是否相等的判断时需要注意。 - -`vm.$watch` 的第一个参数只接收简单的响应式数据的键路径,对于更复杂的表达式建议使用函数作为第一个参数。 - -至于 `vm.$watch` 的内部原理是: -- 设置 options.user = true,标志是一个用户 watcher -- 实例化一个 Watcher 实例,当检测到数据更新时,通过 watcher 去触发回调函数的执行,并传递新老值作为回调函数的参数 -- 返回一个 unwatch 函数,用于取消观察 - -### vm.$on(event, callback) 做了什么? - -监听当前实例上的自定义事件,事件可由 `vm.$emit` 触发,回调函数会接收所有传入事件触发函数`vm.$emit`的额外参数。 - -`vm.$on` 的原理很简单,就是处理传递的 event 和 callback 两个参数,将注册的事件和回调函数以键值对的形式存储到 vm._event 对象中,`vm._events = { eventName: [cb1, cb2, ...], ... }`。 - -### vm.$emit(eventName, [...args]) 做了什么? - -触发当前实例上的指定事件,附加参数都会传递给事件的回调函数。 - -其内部原理就是执行 vm._events[eventName] 中所有的回调函数。 - -> 备注:从 on和on 和 on和emit 的实现原理也能看出,组件的自定义事件其实是谁触发谁监听,所以在这会儿再回头看 [Vue 源码解读(2)—— Vue 初始化过程](https://juejin.cn/post/6950084496515399717) 中关于 initEvent 的解释就会明白在说什么,因为组件自定义事件的处理内部用的就是 vm.on、vm.on、vm.on、vm.emit。 - -### vm.$off([event, callback]) 做了什么? - -移除自定义事件监听器,即移除 `vm._events` 对象上相关数据。 -- 如果没有提供参数,则移除实例的所有事件监听 -- 如果只提供了 event 参数,则移除实例上该事件的所有监听器 -- 如果两个参数都提供了,则移除实例上该事件对应的监听器 - -### vm.$once(event, callback) 做了什么? - -监听一个自定义事件,但是该事件只会被触发一次。一旦触发以后监听器就会被移除。 - -其内部的实现原理是: -- 包装用户传递的回调函数,当包装函数执行的时候,除了会执行用户回调函数之外还会执行 vm.$off(event, 包装函数) 移除该事件 -- 用 vm.$on(event, 包装函数) 注册事件 - -### vm._update(vnode, hydrating) 做了什么? - -官方文档没有说明该 API,这是一个用于源码内部的实例方法,负责更新页面,是页面渲染的入口,其内部根据是否存在 prevVnode 来决定是首次渲染,还是页面更新,从而在调用 __patch__ 函数时传递不同的参数。该方法在业务开发中不会用到。 - -### vm.$forceUpdate() 做了什么? - -迫使 Vue 实例重新渲染,它仅仅影响组件实例本身和插入插槽内容的子组件,而不是所有子组件。其内部原理到也简单,就是直接调用 vm._watcher.update(),它就是 watcher.update() 方法,执行该方法触发组件更新。 - -### vm.$destroy() 做了什么? - -负责完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令和事件监听器。在执行过程中会调用 beforeDestroy 和 destroy 两个钩子函数。在大多数业务开发场景下用不到该方法,一般都通过 v-if 指令来操作。其内部原理是: -- 调用 beforeDestroy 钩子函数 -- 将自己从老爹肚子里($parent)移除,从而销毁和老爹的关系 -- 通过 watcher.teardown() 来移除依赖监听 -- 通过 vm._ _ patch _ _(vnode, null) 方法来销毁节点 -- 调用 destroyed 钩子函数 -- 通过 vm.$off 方法移除所有的事件监听 - -### vm.$nextTick(cb) 做了什么? - -vm.$nextTick 是 Vue.nextTick 的别名,其作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想立即获取更改过后的 DOM 数据: - -```javascript -this.key = 'new val' - -Vue.nextTick(function() { - // DOM 更新了 -}) -``` - -其内部的执行过程是: -- this.key = 'new val',触发依赖通知更新,将负责更新的 watcher 放入 watcher 队列 -- 将刷新 watcher 队列的函数放到 callbacks 数组中 -- 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数 -- vm.$nextTick(cb) 来插队,直接将 cb 函数放入 callbacks 数组 -- 待将来的某个时刻执行刷新 callbacks 数组的函数 -- 然后执行 callbacks 数组中的众多函数,触发 watcher.run 的执行,更新 DOM -- 由于 cb 函数是在后面放到 callbacks 数组,所以这就保证了先完成的 DOM 更新,再执行 cb 函数 - -### vm._render 做了什么? - -官方文档没有提供该方法,它是一个用于源码内部的实例方法,负责生成 vnode。其关键代码就一行,执行 render 函数生成 vnode。不过其中加了大量的异常处理代码。 - - - - -# (7)Hook Event - -[阅读原文](https://juejin.cn/post/6954923081462710309) - -### 什么是 Hook Event? - -Hook Event 是 Vue 的自定义事件结合生命周期钩子实现的一种从组件外部为组件注入额外生命周期方法的功能。 - -### Hook Event 是如果实现的? - -```html - -``` - -- 处理组件自定义事件的时候(vm.$on) 如果发现组件有 hook:xx 格式的事件(xx 为 Vue 的生命周期函数),则将 vm._hasHookEvent 置为 true,表示该组件有 Hook Event - -- 在组件生命周期方法被触发的时候,内部会通过 callHook 方法来执行这些生命周期函数,在生命周期函数执行之后,如果发现 vm._hasHookEvent 为 true,则表示当前组件有 Hook Event,通过 vm.$emit('hook:xx') 触发 Hook Event 的执行 - - - -# (8)编译器 之 解析 - -[阅读原文(上篇)](https://juejin.cn/post/6959019076983209992) - -[阅读原文(下篇)](https://juejin.cn/post/6959019174215548935) - -### 面试官 问:简单说一下 Vue 的编译器都做了什么? - -Vue 的编译器做了三件事情: -- 将组件的 html 模版解析成 AST 对象 -- 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数 -- 从 AST 生成运行时的渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数 - -### 详细说一说编译器的解析过程,它是怎么将 html 字符串模版变成 AST 对象的? - -- 遍历 HTML 模版字符串,通过正则表达式匹配 "<" -- 跳过某些不需要处理的标签,比如:注释标签、条件注释标签、Doctype。 - - 备注:整个解析过程的核心是处理开始标签和结束标签 -- 解析开始标签 - - 得到一个对象,包括 标签名(tagName)、所有的属性(attrs)、标签在 html 模版字符串中的索引位置 - - 进一步处理上一步得到的 attrs 属性,将其变成 [{ name: attrName, value: attrVal, start: xx, end: xx }, ...] 的形式 - - 通过标签名、属性对象和当前元素的父元素生成 AST 对象,其实就是一个 普通的 JS 对象,通过 key、value 的形式记录了该元素的一些信息 - - 接下来进一步处理开始标签上的一些指令,比如 v-pre、v-for、v-if、v-once,并将处理结果放到 AST 对象上 - - 处理结束将 ast 对象存放到 stack 数组 - - 处理完成后会截断 html 字符串,将已经处理掉的字符串截掉 -- 解析闭合标签 - - 如果匹配到结束标签,就从 stack 数组中拿出最后一个元素,它和当前匹配到的结束标签是一对。 - - 再次处理开始标签上的属性,这些属性和前面处理的不一样,比如:key、ref、scopedSlot、样式等,并将处理结果放到元素的 AST 对象上 - - 备注:视频中说这块儿有误,回头看了下,没有问题,不需要改,确实是这样 - - 然后将当前元素和父元素产生联系,给当前元素的 ast 对象设置 parent 属性,然后将自己放到父元素的 ast 对象的 children 数组中 -- 最后遍历完整个 html 模版字符串以后,返回 ast 对象 - - - -# (9)编译器 之 优化 - -[阅读原文](https://juejin.cn/post/6960465810682806308) - -### 简单说一下 Vue 的编译器都做了什么? - -Vue 的编译器做了三件事情: -- 将组件的 html 模版解析成 AST 对象 -- 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数 -- 从 AST 生成运行渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数 - -### 详细说一下静态标记的过程 - -- 标记静态节点 - - 通过递归的方式标记所有的元素节点 - - 如果节点本身是静态节点,但是存在非静态的子节点,则将节点修改为非静态节点 -- 标记静态根节点,基于静态节点,进一步标记静态根节点 - - 如果节点本身是静态节点 && 而且有子节点 && 子节点不全是文本节点,则标记为静态根节点 - - 如果节点本身不是静态根节点,则递归的遍历所有子节点,在子节点中标记静态根 - -### 什么样的节点才可以被标记为静态节点? - -- 文本节点 -- 节点上没有 v-bind、v-for、v-if 等指令 -- 非组件 - - - -# (10)编译器 之 生成渲染函数 - -[阅读原文](https://juejin.cn/post/6961545472204865572) - -### 简单说一下 Vue 的编译器都做了什么? - -Vue 的编译器做了三件事情: -- 将组件的 html 模版解析成 AST 对象 -- 优化,遍历 AST,为每个节点做静态标记,标记其是否为静态节点,然后进一步标记出静态根节点,这样在后续更新的过程中就可以跳过这些静态节点了;标记静态根用于生成渲染函数阶段,生成静态根节点的渲染函数 -- 从 AST 生成运行渲染函数,即大家说的 render,其实还有一个,就是 staticRenderFns 数组,里面存放了所有的静态节点的渲染函数 - -### 详细说一下渲染函数的生成过程 - -大家一说到渲染函数,基本上说的就是 render 函数,其实编译器生成的渲染有两类: -- 第一类就是一个 render 函数,负责生成动态节点的 vnode -- 第二类是放在一个叫 staticRenderFns 数组中的静态渲染函数,这些函数负责生成静态节点的 vnode -渲染函数生成的过程,其实就是在遍历 AST 节点,通过递归的方式,处理每个节点,最后生成形如:`_c(tag, attr, children, normalizationType)` 的结果。tag 是标签名,attr 是属性对象,children 是子节点组成的数组,其中每个元素的格式都是 `_c(tag, attr, children, normalizationTYpe)` 的形式,normalization 表示节点的规范化类型,是一个数字 0、1、2,不重要。 - -在处理 AST 节点过程中需要大家重点关注也是面试中常见的问题有: -- 静态节点是怎么处理的(静态节点的处理分为两步)? - - 将生成静态节点 vnode 函数放到 staticRenderFns 数组中 - - 返回一个 `_m(idx)` 的可执行函数,意思是执行 staticRenderFns 数组中下标为 idx 的函数,生成静态节点的 vnode -- v-once、v-if、v-for、组件 等都是怎么处理的? - - 单纯的 v-once 节点处理方式和静态节点一致 - - v-if 节点的处理结果是一个三元表达式 - - v-for 节点的处理结果是可执行的 `_l` 函数,该函数负责生成 v-for 节点的 vnode - - 组件的处理结果和普通元素一样,得到的是形如 `_c(compName)` 的可执行代码,生成组件的 vnode - -### 碎碎念 - -到这里,Vue 编译器 的源码解读就结束了。相信大家在阅读的过程中不免会产生云里雾里的感觉。这个没什么,编译器这块儿确实是比较复杂,可以说是整个框架最难理解也是代码量最大的一部分了。一定要静下心来多读几遍,遇到无法理解的地方,一定要勤动手,通过示例代码加断点调试的方式帮助自己理解。 - -当你读完几遍以后,这时候情况可能就会好一些,但是有些地方可能还会有些晕,这没事,正常。毕竟这是一个框架的编译器,要处理的东西太多太多了,你只需要理解其核心思想(模版解析、静态标记、代码生成)就可以了。后面会有 手写 Vue 系列,编译器这部分会有一个简版的实现,帮助加深对这部分知识的理解。 - -编译器读完以后,会发现有个不明白的地方:编译器最后生成的代码都是经过 with 包裹的,比如: - -```html -
    -
    {{ item }}
    -
    -``` - -经过编译后生成: - -```javascript -with (this) { - return _c( - 'div', - { - attrs: - { - "id": "app" - } - }, - _l( - (arr), - function (item) { - return _c( - 'div', - { - key: item - }, - [_v(_s(item))] - ) - } - ), - 0 - ) -} -``` - -都知道,with 语句可以扩展作用域链,所以生成的代码中的 `_c`、`_l`、`_v`、`_s` 都是 this 上一些方法,也就是说在运行时执行这些方法可以生成各个节点的 vnode。 - -所以联系前面的知识,响应式数据更新的整个执行过程就是: -- 响应式拦截到数据的更新 -- dep 通知 watcher 进行异步更新 -- watcher 更新时执行组件更新函数 updateComponent -- 首先执行 vm._render 生成组件的 vnode,这时就会执行编译器生成的函数 - -问题: -- 渲染函数中的 _c、_l、、_v、_s 等方法是什么? -- 它们是如何生成 vnode 的? - -下一篇文章 [Vue 源码解读(11)—— render helper]() 将会带来这部分知识的详细解读,也是面试经常被问题的:比如:v-for 的原理是什么? - - - -# (11)render helper - -[阅读原文](https://juejin.cn/post/6963048982079602696) - -### 一个组件是如何变成 VNode? - -- 组件实例初始化,最后执行 $mount 进入挂载阶段 -- 如果是只包含运行时的 vue.js,只直接进入挂载阶段,因为这时候的组件已经变成了渲染函数,编译过程通过模块打包器 + vue-loader + vue-template-compiler 完成的 -- 如果没有使用预编译,则必须使用全量的 vue.js -- 挂载时如果发现组件配置项上没有 render 选项,则进入编译阶段 -- 将模版字符串编译成 AST 语法树,其实就是一个普通的 JS 对象 -- 然后优化 AST,遍历 AST 对象,标记每一个节点是否为静态静态;然后再进一步标记出静态根节点,在组件后续更新时会跳过这些静态节点的更新,以提高性能 -- 接下来从 AST 生成渲染函数,生成的渲染函数有两部分组成: - - 负责生成动态节点 VNode 的 render 函数 - - 还有一个 staticRenderFns 数组,里面每一个元素都是一个生成静态节点 VNode 的函数,这些函数会作为 render 函数的组成部分,负责生成静态节点的 VNode -- 接下来将渲染函数放到组件的配置对象上,进入挂载阶段,即执行 mountComponent 方法 -- 最终负责渲染组件和更新组件的是一个叫 updateComponent 方法,该方法每次执行前首先需要执行 `vm._render` 函数,该函数负责执行编译器生成的 render,得到组件的 VNode -- 将一个组件生成 VNode 的具体工作是由 render 函数中的 `_c`、`_o`、`_l`、`_m` 等方法完成的,这些方法都被挂载到 Vue 实例上面,负责在运行时生成组件 VNode - -> 提示:到这里首先要明白什么是 VNode,一句话描述就是 —— 组件模版的 JS 对象表现形式,它就是一个普通的 JS 对象,详细描述了组件中各节点的信息 - -> 下面说的有点多,其实记住一句就可以了,设置组件配置信息,然后通过 new VNode(组件信息) 生成组件的 VNode - -- `_c`,负责生成组件或 HTML 元素的 VNode,`_c` 是所有 render helper 方法中最复杂,也是最核心的一个方法,其它的 `_xx` 都是它的组成部分 - - 接收标签、属性 JSON 字符串、子节点数组、节点规范化类型作为参数 - - 如果标签是平台保留标签或者一个未知的元素,则直接 new VNode(标签信息) 得到 VNode - - 如果标签是一个组件,则执行 createComponent 方法生成 VNode - - 函数式组件执行自己的 render 函数生成 VNode - - 普通组件则实例化一个 VNode,并且在在 data.hook 对象上设置 4 个方法,在组件的 patch 阶段会被调用,从而进入子组件的实例化、挂载阶段,然后进行编译生成渲染函数,直至完成渲染 - - 当然生成 VNode 之前会进行一些配置处理比如: - - 子组件选项合并,合并全局配置项到组件配置项上 - - 处理自定义组件的 v-model - - 处理组件的 props,提取组件的 props 数据,以组件的 props 配置中的属性为 key,父组件中对应的数据为 value 生成一个 propsData 对象;当组件更新时生成新的 VNode,又会进行这一步,这就是 props 响应式的原理 - - 处理其它数据,比如监听器 - - 安装内置的 init、prepatch、insert、destroy 钩子到 data.hooks 对象上,组件 patch 阶段会用到这些钩子方法 -- `_l`,运行时渲染 v-for 列表的帮助函数,循环遍历 val 值,依次为每一项执行 render 方法生成 VNode,最终返回一个 VNode 数组 -- `_m`,负责生成静态节点的 VNode,即执行 staticRenderFns 数组中指定下标的函数 - -**简单总结 render helper 的作用**就是:在 Vue 实例上挂载一些运行时的工具方法,这些方法用在编译器生成的渲染函数中,用于生成组件的 VNode。 - -好了,到这里,一个组件从初始化开始到最终怎么变成 VNode 就讲完了,最后剩下的就是 patch 阶段了,下一篇文章将讲述如何将组件的 VNode 渲染到页面上。 - - - -# (12)patch - -[阅读原文](https://juejin.cn/post/6964141635856760868) - -### 你能说一说 Vue 的 patch 算法吗? - -Vue 的 patch 算法有三个作用:负责首次渲染和后续更新或者销毁组件 -- 如果老的 VNode 是真实元素,则表示首次渲染,创建整棵 DOM 树,并插入 body,然后移除老的模版节点 -- 如果老的 VNode 不是真实元素,并且新的 VNode 也存在,则表示更新阶段,执行 patchVnode - - 首先是全量更新所有的属性 - - 如果新老 VNode 都有孩子,则递归执行 updateChildren,进行 diff 过程 - - 针对前端操作 DOM 节点的特点进行如下优化: - - 同层比较(降低时间复杂度)深度优先(递归) - - 而且前端很少有完全打乱节点顺序的情况,所以做了四种假设,假设新老 VNode 的开头结尾存在相同节点,一旦命中假设,就避免了一次循环,降低了 diff 的时间复杂度,提高执行效率。如果不幸没有命中假设,则执行遍历,从老的 VNode 中找到新的 VNode 的开始节点 - - 找到相同节点,则执行 patchVnode,然后将老节点移动到正确的位置 - - 如果老的 VNode 先于新的 VNode 遍历结束,则剩余的新的 VNode 执行新增节点操作 - - 如果新的 VNode 先于老的 VNode 遍历结束,则剩余的老的 VNode 执行删除操纵,移除这些老节点 - - 如果新的 VNode 有孩子,老的 VNode 没孩子,则新增这些新孩子节点 - - 如果老的 VNode 有孩子,新的 VNode 没孩子,则删除这些老孩子节点 - - 剩下一种就是更新文本节点 -- 如果新的 VNode 不存在,老的 VNode 存在,则调用 destroy,销毁老节点 - -### 碎碎念 - -好了,到这里,Vue 源码解读系列就结束了,如果你认认真真的读完整个系列的文章,相信你对 Vue 源码已经相当熟悉了,不论是从宏观层面理解,还是某些细节方面的详解,应该都没问题。即使有些细节现在不清楚,但是当遇到问题时,你也能一眼看出来该去源码的什么位置去找答案。 - -到这里你可以试着在自己的脑海中复述一下 Vue 的整个执行流程。过程很重要,但 总结 才是最后的升华时刻。如果在哪个环节卡住了,可再回去读相应的部分就可以了。 - -还记得系列的第一篇文章中提到的目标吗?相信阅读几遍下来,你一定可以在自己的简历中写到:**精通 Vue 框架的源码原理**。 - -接下来会开始 Vue 的手写系列。 - - -> 作者:李永宁 -> -> 链接:https://juejin.cn/user/1028798616461326 -> -> 来源:掘金 -> -> 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 - - -# 作者介绍 -- [个人博客](https://leedebug.github.io/) -- [Github](https://github.com/LeeDebug) -- [掘金](https://juejin.cn/user/2189882894323975/posts) -- [语雀](https://www.yuque.com/LeeDebug) -- [简书](https://www.jianshu.com/u/fc47eb26e53c) -- [开源中国](https://my.oschina.net/LeeDebug) -- [博客园](https://www.cnblogs.com/LeeDebug/) -- 微信二维码: - - - - -
    - WeChat:lcc961150665 -
    - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266.md" "b/source/_posts/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266.md" deleted file mode 100644 index 316d8a7e7..000000000 --- "a/source/_posts/copy-webpack-plugin\345\244\204\347\220\206\345\215\225\347\213\254js\346\226\207\344\273\266.md" +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: copy-webpack-plugin处理单独js文件 -categories: - - Webpack -tags: - - Webpack -keywords: Webpack -description: copy-webpack-plugin处理单独js文件 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210225123255.png' -date: 2021-01-08 22:42:14 ---- - -> 当文件在`static或public`目录下但又想对文件进行编译处理,即可在此插件中进行配置 - -# 使用说明 - -`copy-webpack-plugin`是webpack自带的插件,本意是将某个文件/文件夹,从`dir1`处复制到`dist`下,即当你在运行`npm run build`时,`static或public`目录下的文件就是走的此插件 - -# 配置信息 - -因为我使用的是基于`@vue/cli-service`的`vue3.x`,所以是在`vue.config.js`中设置(如果是vue2.x的版本,请在`webpack.base.js`中设置) - -```js -// 引入 -const CopyWebpackPlugin = require('copy-webpack-plugin') -const UglifyJS = require('uglify-js') - -// 使用 -let config = { - // ...others, - configureWebpack: config => { - config.plugins.push( - // see document: https://www.npmjs.com/package/copy-webpack-plugin/v/5.1.2 - new CopyWebpackPlugin([ - { - from: resolve('./public/handle.js'), // 文件名或目录 - to: './[name].[contenthash].[ext]', // 文件名后添加hash值 - transform(content, path) { // 修改文件内容 - const code = UglifyJS.minify(content.toString()).code; - return code; - }, - transformPath(targetPath, absolutePath) { // 修改文件路径:目标路径、源路径 - newHashPath = targetPath; - return targetPath; - }, - }, - { - from: resolve('./public/index2.html'), - to: './[name].[ext]', - transform(content, path) { - let code = UglifyJS.minify(content.toString()).code; - return code; - }, - force: true, // 覆盖已经存在的文件 - }, - ]) - ); - }, -} -module.exports = config; -``` - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211.md" "b/source/_posts/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211.md" deleted file mode 100644 index b3ddc3cbc..000000000 --- "a/source/_posts/git\344\273\223\345\272\223\346\216\250\351\200\201\350\204\232\346\234\254\357\274\210\346\234\254\345\234\260\347\256\200\346\230\223\347\211\210\357\274\211.md" +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: git仓库推送脚本(本地简易版) -categories: - - Git -tags: - - Git -keywords: Git -description: git仓库推送脚本(本地简易版) -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201226234636.png' -date: 2020-12-26 23:32:34 ---- - -> 由于每次写完代码都要执行一大堆命令来将本地代码推送至远程仓库,所以索性写一个sh脚本 - -# 新建`push.sh`文件: - -在项目主目录,即`/git`所在目录新建`push.sh`文件,并复制以下内容: - -```bash -echo → 暂存选取所有代码 -git add . - -read -p "→ 请输入您的commit提交信息:" MSG - -echo → 提交所有暂存代码 -git commit -m "$MSG" - -echo → 将代码推送至三端git仓库 -git push -u all master - -# 如果不是hexo项目可忽略以下内容 -echo → Hexo自动构建及部署 -npm run clean -npm run build -npm run deploy -``` - -# 运行脚本 - -每次推送时,在当前目录运行`sh push.sh`命令即可,接下来会提示你输入要提交的信息,输入完点击回车即可 - -注:如果加入了hexo项目的构建部署命令,每次推送时也会帮你完成部署 - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225.md" "b/source/_posts/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225.md" deleted file mode 100644 index fad00840c..000000000 --- "a/source/_posts/git\346\216\250\351\200\201\345\220\216\346\262\241\346\234\211\350\264\241\347\214\256\350\256\260\345\275\225.md" +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: git推送后没有贡献记录 -categories: - - Git -tags: - - Git -keywords: Git -description: git推送后没有贡献记录 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201211143223.png' -date: 2020-12-11 12:55:15 ---- - -> github的contribution记录墙不展示问题 - -# 问题背景 - -最近打算把自己的项目同时部署到github、gitlab、gitee上,并且都改成了统一的email邮箱和username用户名,但是发现只有github没有任何的contribution记录。 - -于是我把本地的git信息及mac钥匙串保存的信息都删掉,重新记录了一遍;把SSH KEY也都删掉重新加了一遍。但都么有解决问题。 - -最后发现是github和其他两个仓库用的邮箱不一样,本地git的全局配置邮箱为a,gitee和gitlab用的邮箱也是a,只有github用的邮箱为b;所以github可能认为提交者不是当前用户,所以就没有提交记录。 - -# 解决方案 - -所以将多个仓库的邮箱改为同一个,并且将全局的gitconfig的email也改成此邮箱,即可。 - -# 命令代码 - -查看全局git信息: - -```bash -git config --global --list - -# 或 - -cat ~/.gitconfig -``` - -修改全局的git信息的邮箱: - -```bash -git config --gloabl user.email "your github email" -``` - -配置本仓库的git信息的邮箱: - -```bash -git config user.email "your github email" -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/git\350\264\241\347\214\256\345\242\231gitcalendar.md" "b/source/_posts/git\350\264\241\347\214\256\345\242\231gitcalendar.md" deleted file mode 100644 index 77fcf9e59..000000000 --- "a/source/_posts/git\350\264\241\347\214\256\345\242\231gitcalendar.md" +++ /dev/null @@ -1,630 +0,0 @@ ---- -title: git贡献墙 gitcalendar -categories: - - Git -tags: - - Git -keywords: Git -description: git贡献墙gitcalendar -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201214124257.png' -date: 2020-12-14 12:40:34 ---- - -> 公司为了访问便捷,大都使用gitlab或gitee,所以前两年的贡献度都在这两个仓库上。从今年打算转战github,多做项目、多做整理、多输出文章,旨在提升自己。所以本文的git贡献墙也以github为例 - -# 诸多解决方案 - -## Github Official API - -参考地址: -- [github的贡献墙自带的request](https://github.com/users/LeeDebug/contributions?from=2019-12-01&to=2019-12-14) - -优势: -- 官方的 - -缺点: -- 速度慢 -- 不稳定,可能会挂 -- 单纯的canvas -- 没有可读数据 - -## Github Chart API - -参考地址: -- [github repo](https://github.com/2016rshah/githubchart-api) -- [online demo](https://ghchart.rshah.org/) - -优势: -- 实时性好,与官网同步 - -缺点: -- 速度慢 -- 不稳定,可能会挂 -- 单纯的canvas -- 没有可读数据 - -## github-calendar.js - -参考地址: -- [github repo](https://github.com/Bloggify/github-calendar) -- [online demo](https://bloggify.github.io/github-calendar/example/) - -优势: -- 自带色卡 - -缺点: -- 作者不维护了 - -## GitHub Contribution Calendar API - -参考地址: -- [github repo](https://github.com/rschristian/github-contribution-calendar-api) -- [online demo](https://githubapi.ryanchristian.dev/user/LeeDebug) - -优势: -- 有数据返回,可读信息多 - -缺点: -- 没有图形化展示 -- 实时性差,第二天才更新 - -## Butterfly-gitcalendar - -结合 `github-calendar.js` 和 `GitHub Contribution Calendar API` - -参考地址: -- [github repo](https://github.com/Zfour/Butterfly-gitcalendar) -- [online demo](https://zfour.github.io/Butterfly-gitcalendar/index) - -优势: -- 自定义样式与弹窗 -- 有数据返回,可读信息多 -- 汉化 - -缺点: -- 实时性差,第二天才更新 - - -# 实现方案 - -本次以最后一种解决方案 `Butterfly-gitcalendar` 为基础,进行了部分更改 - -## 步骤一:新增gitcalendar.pug页面 - -在`项目根目录/themes/hexo-theme-butterfly-3.3.0/layout/`文件夹下新建`gitcalendar.pug`文件,并复制以下代码 - -```js -#calendar.calendar - #gitmessage(:style='{top:y+px,left:x+px,position: fixed,zIndex:9999}') - .angle-wrapper - span {{span1}}   - span {{span2}} 次提交 - .position-relative - .border.py-2.graph-before-activity-overview - .js-calendar-graph.mx-md-2.mx-3.d-flex.flex-column.flex-items-end.flex-xl-items-center.overflow-hidden.pt-1.is-graph-loading.graph-canvas.calendar-graph.height-full.text-center(data-graph-url='/users/LeeDebug/contributions?to=2020-10-29', data-url='/LeeDebug', data-from='2019-10-27 00:00:00 UTC', data-to='2020-10-29 23:59:59 UTC', data-org) - #calendarcanvasbox(v-if='simplemode') - canvas#gitcanvas(style='animation: none;') - svg.js-calendar-graph-svg(width='100%', viewBox='0 0 770 128', v-if='!simplemode') - text.month(:x='32 + monthindex*64', y='20', v-for='(month,monthindex) in monthchange') {{month}} - text.wday(text-anchor='start', dx='0', dy='40') 日 - text.wday(text-anchor='start', dx='0', dy='65') 二 - text.wday(text-anchor='start', dx='0', dy='90') 四 - text.wday(text-anchor='start', dx='0', dy='115') 六 - g(v-for='(weekitem,weekIndex) in data', :transform='\'translate(\'+ (16 + weekIndex*14) + \',\' + \'0)\'') - rect(@mouseover="selectStyle(dayitem,$event)" @mouseleave="outStyle()" v-for='(dayitem,dayIndex) in weekitem', :style='{fill:thiscolor(dayitem.count),shapeRendering:crispedges}', :data-score='dayitem.count', :data-date='dayitem.date', x='0', :y=' 30 + dayIndex*13 ', width='11', height='11') - .contrib-footer.clearfix.mt-1.mx-3.px-3.pb-1 - .float-left.text-gray - | 数据来源 - a(:href="'https://github.com/'+ user ", target='blank') @{{user}} - .contrib-legend.text-gray(title='A summary of pull requests, issues opened, and commits to the default and gh-pages branches.') - | Less - - ul.legend - li(:style='{backgroundColor:leeGreen[0]}') - li(:style='{backgroundColor:leeGreen[1]}') - li(:style='{backgroundColor:leeGreen[2]}') - li(:style='{backgroundColor:leeGreen[3]}') - li(:style='{backgroundColor:leeGreen[4]}') - | More - - .contrib-column.contrib-column-first.table-column - span.text-muted 过去一年提交 - span.contrib-number {{total}} - span.text-muted.data-range {{oneyearbeforeday}} - {{thisday}} - .contrib-column.table-column - span.text-muted 最近一月提交 - span.contrib-number {{thisweekdatacore}} - span.text-muted.data-range {{amonthago}} - {{thisday}} - .contrib-column.table-column - span.text-muted 最近一周提交 - span.contrib-number {{weekdatacore}} - span.text-muted.data-range {{aweekago}} - {{thisday}} -``` - -注:请将代码中的两处`LeeDebug`替换为你自己的github用户名 - -## 步骤二:在首页引用gitcalendar.pug - -请打开`项目根目录/themes/hexo-theme-butterfly-3.3.0/layout/index.pug`页面,添加下面三行代码(如有顾虑,请自行备份) - -```js - extends includes/layout.pug - - block content - include ./includes/mixins/post-ui.pug - #recent-posts.recent-posts -+ .recent-post-item(style='width:100%; height: auto;') -+ include gitcalendar.pug -+ .recent-post-item(style='height:0px; clear:both; margin-top: 0px;') - +postUI - include includes/pagination.pug -``` - -前两行为引用`gitcalendar.pug`页面,第3行是为了清除上面的布局样式;如有影响,请删除即可 - -## 步骤三:新增gitcalendar.js脚本 - -在`项目根目录/themes/hexo-theme-butterfly-3.3.0/source/`下,新建文件夹并命名为`gitcalendar`,在此目录下新建文件夹并命名为`js`,在此目录下新建js文件并命名为`gitcalendar.js`(为了区分版本,个人命名为`gitcalendar_v01.js`),并复制以下代码 - -```js -const calendar = new Vue({ - el: '#calendar', - data: { - // 打开时使用canvas绘制gitcalendar,关闭时使用svg绘制gitcalendar - // canvas:dom数少,但图像会发生模糊,自适应一般 svg:dom数多,图像清晰,自适应更佳 - simplemode: false, - // 这里填写你的github用户名 - user: 'LeeDebug', - fixed: 'fixed', - px: 'px', - x: '', - y: '', - span1: '', - span2: '', - month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], - monthchange: [], - oneyearbeforeday: '', - thisday: '', - amonthago: '', - aweekago: '', - weekdatacore: 0, - datacore: 0, - total: 0, - datadate: '', - data: [], - positionplusdata: [], - firstweek: [], - lastweek: [], - beforeweek: [], - thisweekdatacore: 0, - mounthbeforeday: 0, - mounthfirstindex: 0, - crispedges: 'crispedges', - thisdayindex: 0, - amonthagoindex: 0, - amonthagoweek: [], - firstdate: [], - first2date: [], - montharrbefore: [], - monthindex: 0, - leeGreen: ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'], // github的强度颜色 - purple: ['#ebedf0', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f',], - green: ['#ebedf0', '#f0fff4', '#dcffe4', '#bef5cb', '#85e89d', '#34d058', '#28a745', '#22863a', '#176f2c', '#165c26', '#144620'], - blue: ['#ebedf0', '#f1f8ff', '#dbedff', '#c8e1ff', '#79b8ff', '#2188ff', '#0366d6', '#005cc5', '#044289', '#032f62', '#05264c',], - color: ['#ebedf0', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f',] - }, - methods: { - selectStyle(data, event) { - $('.angle-wrapper').show(); - this.span1 = data.date; - this.span2 = data.count; - this.x = event.clientX - 100; - this.y = event.clientY - 60 - }, - outStyle() { - $('.angle-wrapper').hide() - }, - thiscolor(x) { - if (x === 0) { - return this.leeGreen[0] - } else if (x <= 5) { - return this.leeGreen[1] - } else if (x <= 10) { - return this.leeGreen[2] - } else if (x <= 18) { - return this.leeGreen[3] - } else { - return this.leeGreen[4] - } - }, - } -}); -let githubapiurl = "https://githubapi.ryanchristian.dev/user/" + calendar.user; -$(function () { - $.ajax({ - type: "GET", - url: githubapiurl, - dataType: "json", - success: function (data) { - ; - calendar.data = data.contributions; - calendar.total = data.total; - calendar.first2date = calendar.data[48]; - calendar.firstdate = calendar.data[47]; - calendar.firstweek = data.contributions[0]; - calendar.lastweek = data.contributions[52]; - calendar.beforeweek = data.contributions[51]; - calendar.thisdayindex = calendar.lastweek.length - 1; - calendar.thisday = calendar.lastweek[calendar.thisdayindex].date; - calendar.oneyearbeforeday = calendar.firstweek[0].date; - calendar.monthindex = calendar.thisday.substring(5, 7) * 1; - calendar.montharrbefore = calendar.month.splice(calendar.monthindex, 12 - calendar.monthindex); - calendar.monthchange = calendar.montharrbefore.concat(calendar.month); - addweek(); - addlastmonth(); - - function responsiveChart() { - let c = document.getElementById("gitcanvas"); - let cmessage = document.getElementById("gitmessage"); - let ctx = c.getContext("2d"); - c.width = document.getElementById("calendarcanvasbox").offsetWidth; - let linemaxwitdh = 0.96 * c.width / calendar.data.length; - c.height = 9 * linemaxwitdh; - let lineminwitdh = 0.8 * linemaxwitdh; - let setposition = { - x: 0.02 * c.width, - y: 0.025 * c.width - }; - for (let week in calendar.data) { - weekdata = calendar.data[week]; - for (let day in weekdata) { - let dataitem = {date: "", count: "", x: 0, y: 0}; - calendar.positionplusdata.push(dataitem); - ctx.fillStyle = calendar.thiscolor(weekdata[day].count); - // ctx.fillStyle = calendar.leeGreen[weekdata[day].intensity]; // 可按api的强度直接取色 - setposition.y = Math.round(setposition.y * 100) / 100; - dataitem.date = weekdata[day].date; - dataitem.count = weekdata[day].count; - dataitem.x = setposition.x; - dataitem.y = setposition.y; - ctx.fillRect(setposition.x, setposition.y, lineminwitdh, lineminwitdh); - setposition.y = setposition.y + linemaxwitdh - } - ; - setposition.y = 0.025 * c.width; - setposition.x = setposition.x + linemaxwitdh - } - ; - ctx.font = "600 Arial"; - ctx.fillStyle = '#aaa'; - ctx.fillText("日", 0, 1.9 * linemaxwitdh); - ctx.fillText("二", 0, 3.9 * linemaxwitdh); - ctx.fillText("四", 0, 5.9 * linemaxwitdh); - ctx.fillText("六", 0, 7.9 * linemaxwitdh); - let monthindexlist = c.width / 24; - for (let index in calendar.monthchange) { - ctx.fillText(calendar.monthchange[index], monthindexlist, 0.7 * linemaxwitdh); - monthindexlist = monthindexlist + c.width / 12 - } - ; - cmessage.onmousemove = function (event) { - $('.angle-wrapper').hide() - }; - c.onmousemove = function (event) { - $('.angle-wrapper').hide() - getMousePos(c, event); - }; - - function getMousePos(canvas, event) { - var rect = canvas.getBoundingClientRect(); - var x = event.clientX - rect.left * (canvas.width / rect.width); - var y = event.clientY - rect.top * (canvas.height / rect.height); - for (let item of calendar.positionplusdata) { - let lenthx = x - item.x; - let lenthy = y - item.y; - if (0 < lenthx && lenthx < lineminwitdh) { - if (0 < lenthy && lenthy < lineminwitdh) { - $('.angle-wrapper').show(); - calendar.span1 = item.date; - calendar.span2 = item.count; - calendar.x = event.clientX - 100; - calendar.y = event.clientY - 60 - } - } - } - } - } - - responsiveChart(); - $(window).on('resize', responsiveChart); - window.onscroll = function () { - $('.angle-wrapper').hide() - }; - console.log(calendar.positionplusdata) - - function addlastmonth() { - if (calendar.thisdayindex === 0) { - thisweekcore(52); - thisweekcore(51); - thisweekcore(50); - thisweekcore(49); - thisweekcore(48); - calendar.thisweekdatacore += calendar.firstdate[6].count; - calendar.amonthago = calendar.firstdate[6].date - } else { - thisweekcore(52); - thisweekcore(51); - thisweekcore(50); - thisweekcore(49); - thisweek2core(); - calendar.amonthago = calendar.first2date[calendar.thisdayindex - 1].date - } - }; - - function thisweek2core() { - for (let i = calendar.thisdayindex - 1; i < calendar.first2date.length; i++) { - calendar.thisweekdatacore += calendar.first2date[i].count - } - }; - - function thisweekcore(index) { - for (let item of calendar.data[index]) { - calendar.thisweekdatacore += item.count - } - }; - - function addlastweek() { - for (let item of calendar.lastweek) { - calendar.weekdatacore += item.count - } - }; - - function addbeforeweek() { - for (let i = calendar.thisdayindex; i < calendar.beforeweek.length; i++) { - calendar.weekdatacore += calendar.beforeweek[i].count - } - }; - - function addweek() { - if (calendar.thisdayindex === 6) { - calendar.aweekago = calendar.lastweek[0].date; - addlastweek() - } else { - lastweek = data.contributions[51]; - calendar.aweekago = lastweek[calendar.thisdayindex + 1].date; - addlastweek(); - addbeforeweek() - } - } - } - }) -}); -if(document.getElementById("calendarcanvasbox").offsetWidth<500){calendar.simplemode=false} -``` - -注:同理,请将代码中的一处`LeeDebug`替换为你自己的github用户名 - -## 步骤四:新增gitcalendar.css样式 - -在步骤三新增的gitcalendar目录下,即`项目根目录/themes/hexo-theme-butterfly-3.3.0/source/gitcalendar`下,新建文件夹并命名为`css`,在此目录下新建css文件并命名为`gitcalendar.css`(同理,为了区分版本,个人命名为`gitcalendar_v01.css`),并复制以下代码 - -```js -.calendar { - font-family: SourceHanSans-Medium; - border: 1px solid #DDDDDD; - border-radius: 3px; - min-height: 120px; - text-align: center; - margin: 0 auto; - border-width:0px; - width:100%; - display: flex; - display: -webkit-flex; - justify-content: center; - align-items:center; - flex-wrap:wrap; -} -.calendar-graph text.wday, -.calendar-graph text.month { - font-size: 10px; - fill: #aaa; -} -.contrib-legend { - text-align: right; - padding: 0 14px 10px 0; - display: inline-block; - float: right; -} -.contrib-legend .legend { - display: inline-block; - list-style: none; - margin: 0 5px; - position: relative; - bottom: -1px; - padding: 0; -} -.contrib-legend .legend li { - display: inline-block; - width: 10px; - height: 10px; -} -.text-small { - font-size: 12px; - color: #767676; -} -.calendar-graph { - padding: 15px 0 0; - text-align: center; -} -.contrib-column { - text-align: center; - border-left: 1px solid #ddd; - border-top: 1px solid #ddd; - font-size: 11px; -} -.contrib-column-first { - border-left: 0; -} -.table-column { - padding:10px; - display: table-cell; - width:33%; - vertical-align: top; -} -.contrib-number { - font-weight: 300; - line-height: 1.3em; - font-size: 24px; - display: block; -} -.calendar img.spinner { - width: 70px; - margin-top: 50px; - min-height: 70px; -} -.monospace { - text-align: center; - color: #000; - font-family: monospace; -} -.monospace a { - color: #1D75AB; - text-decoration: none; -} -.contrib-footer { - font-size: 12px; - padding: 0 12px 12px; - text-align: left; - width: 100%; - box-sizing: border-box; - height: 26px; -} -.left.text-muted { - float: left; - margin-left: 9px; - color: #767676; -} -.left.text-muted a { - color: #4078c0; - text-decoration: none; -} -.left.text-muted a:hover, -.monospace a:hover { - text-decoration: underline; -} -h2.f4.text-normal.mb-3 { - display: none; -} - -.float-left.text-gray { - float: left; -} -#user-activity-overview{ - display:none; -} -.day-tooltip { - white-space: nowrap; - position: absolute; - z-index: 99999; - padding: 10px; - font-size: 12px; - color: #959da5; - text-align: center; - background: rgba(0,0,0,.85); - border-radius: 3px; - display: none; - pointer-events: none; -} -.day-tooltip strong { - color: #dfe2e5; -} -.day-tooltip.is-visible { - display: block; -} -.day-tooltip:after { - position: absolute; - bottom: -10px; - left: 50%; - width: 5px; - height: 5px; - box-sizing: border-box; - margin: 0 0 0 -5px; - content: " "; - border: 5px solid transparent; - border-top-color: rgba(0,0,0,.85) -} -.position-relative { - width:100%; - padding-left:20px; - padding-right:20px; -} -@media screen and (max-width: 650px){ - .contrib-footer{ - padding: 0; - } - .contrib-column{ - /* display:none */ - line-height: initial; - padding: 5px; - } - .contrib-column .contrib-number{ - font-size: 14px; - font-weight: 500; - } - .contrib-column .data-range{ - display:none - } -} -.angle-wrapper { - z-index:9999; - display:inline; - display:none; - width: 200px; - height: 40px; - position: relative; - padding: 5px 0; - background: rgba(0, 0, 0, 0.8); - border-radius: 8px; - text-align: center; - color: white; -} -.angle-box { - position:fixed; - padding:10px -} -.angle-wrapper span{ - padding-bottom:1em; -} -.angle-wrapper:before { - content: ''; - width: 0; - height: 0; - border: 10px solid transparent; - border-top-color: rgba(0, 0, 0, 0.8); - position: absolute; - left: 47.5%; - top: 100%; -} -``` - -## 引入js、css代码,即vue的依赖 - -在`项目根目录/themes/hexo-theme-butterfly-3.3.0/_config.yml`文件内,找到`inject`处,并添加以下3行代码 - -```js -inject: - head: -+ - - bottom: -+ - -+ - -``` - -注:此处的js和css文件也可改为cdn引入,但要注意缓存问题。另,如无特殊需求,请引入`vue@2.6.11`,非必要请不要修改 - -## 步骤五:打包部署 - -教程到此结束,打包部署即可查看效果。如有疑问请联系博主 - - -# 祝君无Bug~ \ No newline at end of file diff --git a/source/_posts/hello-world.md b/source/_posts/hello-world.md deleted file mode 100644 index cdc04a8e1..000000000 --- a/source/_posts/hello-world.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Hello World -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170450.png ---- -Welcome to [Hexo](https://hexo.io/)! This is your very first post. Check [documentation](https://hexo.io/docs/) for more info. If you get any problems when using Hexo, you can find the answer in [troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me on [GitHub](https://github.com/hexojs/hexo/issues). - -## Quick Start - -### Create a new post - -``` bash -$ hexo new "My New Post" -``` - -More info: [Writing](https://hexo.io/docs/writing.html) - -### Run server - -``` bash -$ hexo server -``` - -More info: [Server](https://hexo.io/docs/server.html) - -### Generate static files - -``` bash -$ hexo generate -``` - -More info: [Generating](https://hexo.io/docs/generating.html) - -### Deploy to remote sites - -``` bash -$ hexo deploy -``` - -More info: [Deployment](https://hexo.io/docs/one-command-deployment.html) - - -# 祝君无Bug~ \ No newline at end of file diff --git a/source/_posts/js-md5.md b/source/_posts/js-md5.md deleted file mode 100644 index 09b870400..000000000 --- a/source/_posts/js-md5.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: js-md5 -categories: - - npm -tags: - - npm -keywords: 'npm' -description: 前端插件 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170415.png -date: 2020-06-10 22:49:18 ---- - -> 前端在用户登录时尝使用md5对用户的登录信息进行加密操作 - -# MD5简介 -MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。 - -# js-md5简介 -A simple MD5 hash function for JavaScript supports UTF-8 encoding. -详情请查看npm的[js-md5](https://www.npmjs.com/package/js-md5)部分 - -# 使用教程 - -## 安装 -``` -$ npm i js-md5 -``` - -## 引入 -``` -$ import md5 from 'js-md5' - -// 如果在main.js中引入,需要注册 -$ Vue.prototype.$md5 = md5 -``` - -## 使用 -``` -md5(''); // d41d8cd98f00b204e9800998ecf8427e -md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6 -md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0 - -// It also supports UTF-8 encoding -md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07 - -// It also supports byte `Array`, `Uint8Array`, `ArrayBuffer` -md5([]); // d41d8cd98f00b204e9800998ecf8427e -md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e - -// Different output -md5(''); // d41d8cd98f00b204e9800998ecf8427e -md5.hex(''); // d41d8cd98f00b204e9800998ecf8427e -md5.array(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126] -md5.digest(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126] -md5.arrayBuffer(''); // ArrayBuffer -md5.buffer(''); // ArrayBuffer, deprecated, This maybe confuse with Buffer in node.js. Please use arrayBuffer instead. -md5.base64(''); // 1B2M2Y8AsgTpgAmY7PhCfg== -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git a/source/_posts/jsdelivr.md b/source/_posts/jsdelivr.md deleted file mode 100644 index 14036baf6..000000000 --- a/source/_posts/jsdelivr.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: jsdelivr -categories: - - Git -tags: - - Git -keywords: Git -description: A free CDN for Open Source -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111165611.png -date: 2020-11-10 18:14:26 ---- - -> 本文概要 - -# CDN - -## [jsdelivr for GitHub](https://www.jsdelivr.com/?docs=gh) - -需要配合PicGo图床使用 -- 使用PicGo客户端上传图片 -- 复制url - - 默认格式为:`PicGo默认前缀 + GitHub用户名 + / + 仓库名 + / + 分支名 + 图片在GitHub仓库的绝对路径` - - 例如:`https://raw.githubusercontent.com/LeeDebug/PicGo/master/img/xxx.png` -- 替换url中的前缀部分 - - 默认格式为`jsdeliver默认前缀 + 图片在GitHub仓库的绝对路径` - - 例如:`https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/xxx.png` - -测试图片: -![GitHub自带的CND](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111165611.png) - - -# 图床 - -## [PicGo+GitHub](https://molunerfinn.com/PicGo/) - -支持桌面客户端上传、快捷键上传;仅支持普通url复制; - -参考文档: -- [PicGo+GitHub图床,让Markdown飞](https://www.jianshu.com/p/2756724a5dee) -- [图床工具的使用-PicGo](https://www.jianshu.com/p/9d91355e8418) - -测试图片: -![PicGo+GitHub](https://raw.githubusercontent.com/LeeDebug/PicGo/master/img/20201111165611.png) - -## [新浪图床](https://chrome.google.com/webstore/detail/%E6%96%B0%E6%B5%AA%E5%BE%AE%E5%8D%9A%E5%9B%BE%E5%BA%8A/fdfdnfpdplfbbnemmmoklbfjbhecpnhf) - -chrome插件上传;支持多种格式url复制; - -测试图片: -![新浪图床](http://ww1.sinaimg.cn/large/c491e5a7ly1gkj4gezdtcj20a305xq2y.jpg) - -## [搜狗图床](http://www.urkeji.com/tuchuang/sg/) - -网页上传;不支持url复制; - -测试图片: -![搜狗图床](https://img02.sogoucdn.com/app/a/100520146/eb05b038aeeef681ab204d37c3ca6ba8) - -## [奇虎图床](http://www.urkeji.com/tuchuang/qh/) - -网页上传;不支持url复制; - -测试图片: -![奇虎图床](http://p5.so.qhimgs1.com/t02aeeef681ab204d37.jpg) - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230.md" "b/source/_posts/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230.md" deleted file mode 100644 index 7338778a7..000000000 --- "a/source/_posts/linear-gradient\345\207\275\346\225\260\345\256\236\347\216\260\347\272\277\346\200\247\346\270\220\345\217\230.md" +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: linear-gradient函数实现线性渐变 -categories: - - CSS -tags: - - CSS -keywords: CSS -description: linear-gradient函数实现线性渐变 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210302102414.png' -date: 2021-02-21 10:23:14 ---- - -> 当tab标签超出滚动时,在侧边增加一个渐变透明的框,会使视觉上更加明显,让用户感知到该处可以滑动 - -# 效果对比展示 - -原本样式: - -![原本样式](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210302103343.png) - -增加渐变的透明框后的样式: - -![增加渐变的透明框后的样式](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210302103521.png) - -滚动后的效果: - -![滚动后的效果](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210302103619.png) - - -# 可滑动的tab标签 - -```css -/* 超出部分可所有滑动 */ -.tab_scroll { - display: -webkit-box; - overflow: auto; -} -/* 隐藏滚轮 */ -.tab_scroll::-webkit-scrollbar { - display: none; -} -``` - - -# 渐变透明的小方块 - -```css -.tab_scroll:before { - content: ''; - position: absolute; - right: 0; - float: right; - /* border: 1px solid red; */ - height: 66px; - width: 60px; - background: linear-gradient(to right, transparent, white); -} -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257.md" "b/source/_posts/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257.md" deleted file mode 100644 index c04024335..000000000 --- "a/source/_posts/navigator.userAgent\350\216\267\345\217\226\345\275\223\345\211\215\350\256\276\345\244\207\344\277\241\346\201\257.md" +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: navigator.userAgent获取当前设备信息 -categories: - - 移动端 -tags: - - 移动端 -keywords: 移动端 -description: navigator.userAgent获取当前设备信息 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210226130414.png' -date: 2021-02-17 23:14:02 ---- - -> 获取当前是何设备,来区分不同的事件及渲染模式 - -# navigator.userAgent - -封装好函数直接调用,利用switch直接进行判断即可 - -```js -export function currDevice() { - const u = navigator.userAgent; - const app = navigator.appVersion; // appVersion 可返回浏览器的平台和版本信息。该属性是一个只读的字符串。 - const browserLang = (navigator.browserLanguage || navigator.language).toLowerCase(); //获取浏览器语言 - const deviceBrowser = (() => { - return { - trident: u.indexOf('Trident') > -1, // IE内核 - presto: u.indexOf('Presto') > -1, // opera内核 - webKit: u.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核 - gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1, // 火狐内核 - mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端 - ios: !!u.match(/\(i[^;]+;( U;)? CPU.Mac OS X/), // ios终端 - android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, // android终端或者uc浏览器 - iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器 - iPad: u.indexOf('iPad') > -1, // 是否iPad - webApp: u.indexOf('Safari') === -1, // 是否web应用程序,没有头部和底部 - weixin: u.indexOf('MicroMessenger') > -1, // 是否微信 - qq: u.match(/\sQQ/i) === "qq", // 是否QQ - }; - })(); - return deviceBrowser; -} -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272.md" "b/source/_posts/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272.md" deleted file mode 100644 index 68c623358..000000000 --- "a/source/_posts/node\346\211\223\345\214\205\345\206\205\345\255\230\346\272\242\345\207\272.md" +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: node打包内存溢出 -categories: - - Webpack -tags: - - Webpack -keywords: Webpack -description: node打包内存溢出 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201211230124.png' -date: 2020-12-11 22:55:31 ---- - -> vue项目利用webpack打包时提示内存堆栈溢出 - -# 报错信息 - -```bash -FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory -``` - -# 解决方案 - -在build前,根据自己的项目大小设置一下最大分配内存空间,即可。 - -```js -// 在package.json中 -"scripts": { - ... - "build": "node --max_old_space_size=4096 build/build.js" -} -``` - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225.md" "b/source/_posts/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225.md" deleted file mode 100644 index a6e49798e..000000000 --- "a/source/_posts/qiankun-2-0-24-\347\210\254\345\235\221\350\256\260\345\275\225.md" +++ /dev/null @@ -1,707 +0,0 @@ ---- -title: qiankun 2.0.24 爬坑记录 -categories: - - 微前端 -tags: - - 微前端 -keywords: '微前端' -description: 描述 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170023.png -date: 2020-10-27 23:45:08 ---- - -> 由于本次开发项目需要嵌入之前的老项目,由于考虑到iframe速度慢、css/js需要额外请求、阻塞页面加载、浏览器前进/后退等缺点,遂打算踩坑qiankun,为了更早的爬坑,整理此文。 - -# 简介 -[qiankun](https://github.com/umijs/qiankun) 是一个基于 [single-spa](https://github.com/single-spa/single-spa) 的`微前端`实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。 - -**官方提供的资源:** -- 官方提供了一个 [示例代码](https://github.com/fengxianqi/qiankun-example) -- 示例代码的[在线demo](http://qiankun.fengxianqi.com/) -- 单独访问在线[vue子应用](http://qiankun.fengxianqi.com/subapp/sub-vue/) -- 单独访问在线[react子应用](http://qiankun.fengxianqi.com/subapp/sub-react/) - -**根据 [qiankun官方文档](https://qiankun.umijs.org/zh/guide#%E7%89%B9%E6%80%A7) 介绍,主要有以下七大特性:** -- 📦 基于 single-spa 封装,提供了更加开箱即用的 API。 -- 📱 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。 -- 💪 HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。 -- 🛡​ 样式隔离,确保微应用之间样式互相不干扰。 -- 🧳 JS 沙箱,确保微应用之间 全局变量/事件 不冲突。 -- ⚡️ 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。 -- 🔌 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。 - - -**行业内其他前端团队对微前端的看法和实践:** -- [每日优鲜供应链前端团队微前端改造](https://juejin.im/post/6844903943873675271) -- [微前端在美团外卖的实践](https://juejin.im/post/6844904073972432903) -- [前端微服务在字节跳动的打磨与应用](https://juejin.im/post/6844904046487142408) -- [微前端在小米 CRM 系统的实践](https://juejin.im/post/6844904112421765134) -- [标准微前端架构在蚂蚁的落地实践](https://developer.aliyun.com/article/742576) - - -# API介绍 - -此处只介绍api的简单功能描述,如想继续了解请移步[官方文档](https://qiankun.umijs.org/zh/api) - -## registerMicroApps(apps, lifeCycles?) - -注册微应用的基础配置信息。当浏览器 url 发生变化时,会自动检查每一个微应用注册的 activeRule 规则,符合规则的应用将会被自动激活。 - -```js -import { registerMicroApps } from 'qiankun'; - -registerMicroApps( - [ - { - // name - string - 必选,微应用的名称,微应用之间必须确保唯一 - name: 'apass-micro', - // entry - string - 必选,微应用的入口 - entry: 'localhost:8080', - // container - string | HTMLElement - 必选,微应用的容器节点的选择器或者 Element 实例 - container: '#apassMicroTemplateConfig', - // activeRule - string - 必选,微应用的激活规则 - activeRule: '/index/config/template/edit', - // props - object - 可选,主应用需要传递给微应用的数据 - props: { - name: 'kuitos', - routerPushFunc: (that) => { - that.$router.push('/713/5f4f65fabcb7c173/fields') - }, - data: { - // 已响应式的数据通信 - store: microAppStore.getGlobalState - }, - } - } - ], - { - beforeLoad: app => console.log('before load', app.name), - beforeMount: [ - app => console.log('before mount', app.name), - ], - afterMount: [ - app => console.log('after mount', app.name), - ], - beforeUnmoun: [ - app => console.log('before unmount', app.name), - ], - afterUnmount: [ - app => console.log('after unmount', app.name), - ] - }, -); -``` - -## start(opts?) - -启动 qiankun - -```js -import { start } from 'qiankun'; - -start(); -``` - -## setDefaultMountApp(appLink) - -设置主应用启动后默认进入的微应用。 - -```js -import { setDefaultMountApp } from 'qiankun'; - -setDefaultMountApp('/homeApp'); -``` - -## runAfterFirstMounted(effect) - -第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。 - -```js -import { runAfterFirstMounted } from 'qiankun'; - -runAfterFirstMounted(() => { - console.log('第一个子应用加载完后,该方法被调用') - this.otherFunction() -}) -``` - -## loadMicroApp(app, configuration?) - -适用于需要手动 加载/卸载 一个微应用的场景。 - -通常这种场景下微应用是一个不带路由的可独立运行的业务组件。 微应用不宜拆分过细,建议按照业务域来做拆分。业务关联紧密的功能单元应该做成一个微应用,反之关联不紧密的可以考虑拆分成多个微应用。 一个判断业务关联是否紧密的标准:看这个微应用与其他微应用是否有频繁的通信需求。如果有可能说明这两个微应用本身就是服务于同一个业务场景,合并成一个微应用可能会更合适。 - -```js -import { loadMicroApp } from 'qiankun'; - -// 因为loadMicroApp()返回子应用的实例,拿一个全局变量接收后续可进行其他操作如:手动卸载子应用 -this.microApp = loadMicroApp( - { - name: 'sub-vue', - entry: 'http://localhost:7777/subapp/sub-vue', - container: '#apassMicroTemplateConfig', - props: { - routerBase: '/index/config/template/edit', - getGlobalState: microAppStore.getGlobalState, - sheetId: '2133123123' - } - }, - { - // sandbox - boolean | { strictStyleIsolation?: boolean, experimentalStyleIsolation?: boolean } - 可选,是否开启沙箱,默认为 true - sandbox: { strictStyleIsolation: true }, - // singular - boolean | ((app: RegistrableApp) => Promise); - 可选,是否为单实例场景,单实例指的是同一时间只会渲染一个微应用。默认为 false - singular: true - } -) - -// 封装卸载子应用的函数 -private unmountMicroApp () { - if (this.microApp) { - this.microApp.mountPromise.then(() => { - this.microApp.unmount() - }) - } -} -``` - -## prefetchApps(apps, importEntryOpts?) - -手动预加载指定的微应用静态资源。仅`手动加载`微应用场景需要,基于路由自动激活场景直接配置 prefetch 属性即可。 - -```js -import { prefetchApps } from 'qiankun'; - -prefetchApps([ { name: 'app1', entry: '//locahost:7001' }, { name: 'app2', entry: '//locahost:7002' } ]) -``` - - -# 主应用配置 - -## 安装qiankun - -```bash -$ npm i qiankun -S # 或者 yarn add qiankun -``` - -## 调整main.js - -如果你需要在项目初始化的时候就加载这些子应用,那么需要修改main.js的一些配置;如果是在页面中手动加载可略过此步。 - -```js -import Vue from "vue" -import App from "./App.vue" -import router from "./router" - -import { registerMicroApps, setDefaultMountApp, start } from "qiankun" -Vue.config.productionTip = false -let app = null; -/** - * 渲染函数 - * appContent 子应用html内容 - * loading 子应用加载效果,可选 - */ -function render({ appContent, loading } = {}) { - if (!app) { - app = new Vue({ - el: "#container", - router, - data() { - return { - content: appContent, - loading - }; - }, - render(h) { - return h(App, { - props: { - content: this.content, - loading: this.loading - } - }); - } - }); - } else { - app.content = appContent; - app.loading = loading; - } -} - -/** - * 路由监听 - * @param {*} routerPrefix 前缀 - */ -function genActiveRule(routerPrefix) { - return location => location.pathname.startsWith(routerPrefix); -} - -function initApp() { - render({ appContent: '', loading: true }); -} - -initApp(); - -// 传入子应用的数据 -let msg = { - data: { - auth: false - }, - fns: [ - { - name: "_LOGIN", - _LOGIN(data) { - console.log(`父应用返回信息${data}`); - } - } - ] -}; - -// 注册子应用 -registerMicroApps( - [ - { - name: "sub-app-1", - entry: "//localhost:8091", - render, - activeRule: genActiveRule("/app1"), - props: msg - }, - { - name: "sub-app-2", - entry: "//localhost:8092", - render, - activeRule: genActiveRule("/app2"), - } - ], - { - beforeLoad: [ - app => { - console.log("before load", app); - } - ], // 挂载前回调 - beforeMount: [ - app => { - console.log("before mount", app); - } - ], // 挂载后回调 - afterUnmount: [ - app => { - console.log("after unload", app); - } - ] // 卸载后回调 - } -); - -// 设置默认子应用,与 genActiveRule中的参数保持一致 -setDefaultMountApp("/app1"); - -// 启动 -start(); -``` - -## 修改App.vue中的id 或 增加渲染子应用的盒子 - -因为一个主应用可能会嵌套多个子应用,所以App.vue难免会重名,所以最好加一个自己项目名称的前缀来做区分。 - -```html - - - -``` - - - -# 配置vue子应用 - -因为子应用本身就是一个单独的应用,所以不必安装qiankun,只需要暴露被当做子应用嵌入时,qiankun所需的3个生命周期即可。 - -## 配置maim.js - -在支持被当做子应用嵌入的同时,需要支持项目独立运行,兼容之前配置 - -```js -import Vue from 'vue'; -import VueRouter from 'vue-router'; -import App from './App.vue'; -import routes from './router'; -import './public-path'; - -Vue.config.productionTip = false; - -let router = null; -let instance = null; - -function render() { - router = new VueRouter({ - base: window.__POWERED_BY_QIANKUN__ ? '/app1' : '/', - mode: 'history', - routes, - }); - - instance = new Vue({ - router, - render: h => h(App), - beforeMount () { - if (window.__POWERED_BY_QIANKUN__) { - routerPushFunc(this) - AppModule.SET_CURRENT_ENV() - } - } - }).$mount(container ? container.querySelector('#templateConfig') : '#templateConfig'); -} - -if (!window.__POWERED_BY_QIANKUN__) { - render(); -} - -export async function bootstrap() { - console.log('vue app bootstraped'); -} - -export async function mount(props) { - console.log('props from main app', props); - render(); -} - -export async function unmount() { - (instance as Vue).$destroy(); - (instance as Vue).$el.innerHTML = ''; // 防止内存泄漏,子项目销毁时清空dom - instance = null; - router = null; -} -``` - -## public-path.js - -使用 webpack 静态 publicPath 配置:可以通过两种方式设置,一种是直接在 mian.js 中引入 public-path.js 文件,一种是在开发环境直接修改 vue.config.js - -```js -if (window.__POWERED_BY_QIANKUN__) { - // eslint-disable-next-line no-undef - __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ -} -``` - -## 配置 vue.config.js - -子应用必须支持跨域:由于 qiankun 是通过 fetch 去获取子应用的引入的静态资源的,所以必须要求这些静态资源支持跨域 - -```js -const path = require('path'); -const { name } = require('./package'); - -function resolve(dir) { - return path.join(__dirname, dir); -} - -const pagesMicro = { - templateConfig: { - entry: 'src/microPage/templateConfig/main.ts', - template: 'src/microPage/templateConfig/index.html', - chunks: ['runtime~templateConfig', 'chunk-vendors', 'chunk-common', 'templateConfig'] - }, -} - -const pagesMain = { - index: { - entry: 'src/main.ts', - template: '/index.html' - } -} - -const pages = process.env.VUE_APP_ENTRY === 'main' ? pagesMain : pagesMicro - -let config = { - /** - * You will need to set publicPath if you plan to deploy your site under a sub path, - * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, - * then publicPath should be set to "/bar/". - * In most cases please use '/' !!! - * Detail: https://cli.vuejs.org/config/#publicpath - */ - outputDir: 'dist', - assetsDir: 'static', - filenameHashing: true, - // tweak internal webpack configuration. - // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md - devServer: { - // host: '0.0.0.0', - hot: true, - disableHostCheck: true, - port, - overlay: { - warnings: false, - errors: true, - }, - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }, - // 自定义webpack配置 - configureWebpack: { - resolve: { - alias: { - '@': resolve('src'), - }, - }, - output: { - // 把子应用打包成 umd 库格式 - library: `${name}-[name]`, - libraryTarget: 'umd', - jsonpFunction: `webpackJsonp_${name}`, - }, - }, -}; - -if (process.env.VUE_APP_ENTRY === 'micro') { - config.pages = pagesMicro -} - -module.exports = config -``` - - -# qiankun常见问题及解决方案 - -## 避免 css 污染 - -qiankun 只能解决子项目之间的样式相互污染,不能解决子项目的样式污染主项目的样式,技术与规范方面大约有这 5 种方案: - -- vue自带的scope - - 只能解决一部分页面内的样式污染,但一般不会有这个问题 -- BEM命名方式 -- css-in-js - - 学习曲线高;可读性差;借助前端堆栈消耗性能; -- css-loader - - 开启css-modules,类似于图片懒加载,替换attr - - 缺点:页面中需要把class写成css-modules的形式;样式多了之后都是hash的形式可读性不高; -- postcss-loader - - 利用postcss-modules插件的getJson()函数将所有css文件中的class转为json对象;利用postcss-html把json对象渲染回html页面的class - - 缺点:利用新的gulp,意义不大;每次修改都要编译,很慢; - -**拿css-loader举例,开启css-modules,可参考以下文章:** - -- [阮一峰的 CSS Modules 用法教程](http://www.ruanyifeng.com/blog/2016/06/css_modules.html) -- [CSS Modules 基本用法](https://blog.csdn.net/qq_26733915/article/details/54313492) -- [浅谈CSS Modules以及CSS Modules在Vue.js上的使用](https://blog.csdn.net/weixin_44869002/article/details/105806021) -- [css 命名:BEM, scoped css, css modules 与 css-in-js](https://juejin.im/post/6844903748926439431) -- [Vue CLI 的 CSS相关配置](https://cli.vuejs.org/zh/guide/css.html#%E5%BC%95%E7%94%A8%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90) -- [css-loader 的 github](https://github.com/webpack-contrib/css-loader) -- [css-modules 的 github](https://github.com/css-modules/css-modules) -- [TypeScript 中使用 CSS Modules](https://juejin.im/post/6844903497532473352) - -```js -module.exports = { - // ... 省略其他配置 - css: { - // 是否使用css分离插件 ExtractTextPlugin - extract: false, - // 开启 CSS source maps? - sourceMap: false, - // css预设器配置项 - loaderOptions: { - css: { - // These properties are valid: - // object { url?, import?, modules?, sourceMap?, importLoaders?, localsConvention?, onlyLocals?, esModule? } - modules: { - // These properties are valid: - // object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } - exportGlobals: true, - localIdentName: '[path][name]__[local]--[hash:base64:5]' - }, - localsConvention: 'asIs' // asIs camelCase camelCaseOnly dashes dashesOnly - } - }, - // 启用 CSS modules for all css / pre-processor files. - requireModuleExtension: true - }, -} -``` - -## 谨慎使用 position:fixed - -在子项目中这个定位会出现问题,基本出现在模态框和抽屉的定位上,应尽量避免使用,确有相对于浏览器窗口定位需求,可以用 `position: sticky`,但是会有兼容性问题(IE不支持)。如果定位使用的是 bottom 和 right,则问题不大。 -还有个办法,位置可以写成动态绑定 style 的形式: - -```html -
    -``` - -## 给 body 、 document 等绑定的事件,请在 unmount 周期清除 - -js 沙箱只劫持了 window.addEventListener,使用 document.body.addEventListener 或者 document.body.onClick 添加的事件并不会被沙箱移除,会对其他的页面产生影响,请在 unmount 周期清除 - -## 报错:Uncaught Error application 'xxx' died in status LOADING_SOURCE_CODE: [qiankun] You need to export lifecycle functions in xxx entry - -一般就是打包姿势不对,可能原因:未打包成umd格式;所需的js文件虽然被整体打包了但没被加载,需要利用runtimeChunk单独打包出来 - -## 现刷新页面报错,容器找不到 - -解决方案1:在组件 mounted 周期注册并启动 qiankun - -解决方案2:new Vue() 之后,等 DOM 加载好了再注册并启动 qiankun - -```js -const vueApp = new Vue({ - router, - store, - render: h => h(App) -}).$mount("#app"); -vueApp.$nextTick(() => { - //在这里注册并启动 qiankun -}) -``` - -## 主、子应用的路由,均可用 history 模式 - -因为vue-router的history模式是全匹配的,所以如果当前子应用是被qiankun嵌入时,需要在子应用的一级路由前加上主应用除了`http://ip+port/`后的所有路由,即在主应用中初始子应用是定义的`activeRule`。 - -```js -router = new VueRouter({ - base: window.__POWERED_BY_QIANKUN__ ? '/templateConfig' : '/', - mode: 'history', - routes: [ - { ... } - ] -}) -``` - -## history模式下,主、子应用的路由配置问题 - -如果主、子应用的vue-router都是history模式(即路由全匹配)时 -- 主应用中的route信息的path属性需要改为'index/edit*'的形式,即模糊全匹配,而且子应用的跟路由需要改为'index/edit/'的形式(上面说过了)。否则子应用改变路由后,主应用匹配不到当前页面,则会跳回登录页会调至404。 -- 子应用中的route信息里最好不要有''或者'*'之类的判空。否则主应用(从嵌入子应用的那个页面)跳转到其他页面后,会触发子应用的路由匹配规则,进而跳转至子应用的登录页,而且导致主应用的路由跳转失败(也不能叫失败,实际上是跳转出去了又被redirect重定向回来了)。 - -## 从一个子项目跳转到另一个子项目 - -在子项目里面如何跳转到另一个子项目/主项目页面呢,直接写 或者用 router.push/router.replace 是不行的,原因是这个 router 是子项目的路由,所有的跳转都会基于子项目的 base 。写 链接可以跳转过去,但是会刷新页面,用户体验不好。 - -解决办法也比较简单,在子项目注册时将主项目的路由实例对象传过去,子项目挂载到全局,用父项目的这个 router 跳转就可以了。 - -但是有一丢丢不完美,这样只能通过 js 来跳转,跳转的链接无法使用浏览器自带的右键菜单 - -## 图片资源报错404 - -最好改为绝对路径 - -```html - - - -``` - -或者在主应用中配置nginx静态文件的代理(这里没有后台的nginx配置,所以拿webpack自带的proxyTable代理作示例) - -```js -if (item === '/index/config/template/edit/static') { // 登录页img - proxyObj[item] = { - target: 'http://localhost:8081', - ws: false, - changeOrigin: true, - pathRewrite: { '^/index/config/template/edit/static': '/static' } - } - } else if (item === '/static/home') { // 首页img - proxyObj[item] = { - target: 'http://localhost:8081', - ws: false, - changeOrigin: true, - pathRewrite: { '^/static/home': '/static/home' } - } -} -``` - -## 手动加载子应用时,如果子应用的js文件太大会造成阻塞 - -如果是手动加载子应用,即loadMicroApp(),推荐在页面初始化的时候就预加载资源,即prefetchApps()。避免请求的pending时间太长阻塞加载 - - -## ts项目与js项目文件加载的问题 - -因为主项目是ts,默认加载的是ts文件;但子项目是js。所以在子项目中引入js文件的时候要标清楚后缀名,例如 -``` -// 会报错 Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option. -import {widgetInRecord as widget} from '@/views/sheetConfig/fieldConfig/widget/widget' - -// 加上后缀名就不报错了 -import {widgetInRecord as widget} from '@/views/sheetConfig/fieldConfig/widget/widget.js' -``` - - -## 在一个页面内以不同的初始化数据加载同一子应用(如:左侧是列表,右侧的详情是qiankun嵌入的子应用) - -重复加载问题、数据通信问题、请求响应问题 - -``` -``` - -## 主项目与子项目的数据通信 - -项目之间的不要有太多的数据依赖,毕竟项目还是要独立运行的。通信操作需要判断是否 qiankun 模式,做兼容处理。 - -通过 props 传递父项目的 Vuex ,如果子项目是 vue 技术栈,则会很好用。假如子项目是 jQuery/react/angular ,就不能很好的监听到数据的变化。 - -qiakun 提供了一个全局的 GlobalState 来共享数据。主项目初始化之后,子项目可以监听到这个数据的变化,也能提交这个数据。 - -```js -// 主项目初始化 -import { initGlobalState } from 'qiankun'; - -const actions = initGlobalState(state); - -// 主项目项目监听和修改 -actions.onGlobalStateChange((state, prev) => { - // state: 变更后的状态; prev 变更前的状态 - console.log(state, prev); -}); - -actions.setGlobalState(state); - -// 子项目监听和修改 -export function mount(props) { - props.onGlobalStateChange((state, prev) => { - // state: 变更后的状态; prev 变更前的状态 - console.log(state, prev); - }); - props.setGlobalState(state); -} -``` - -## vue子项目内存泄露问题 - -这个问题挺难发现的,是在 qiankun 的 issue 区看到的: github.com/umijs/qiank… ,排查过程我就不发了,解决方案挺简单。 - -子项目销毁时清空 dom 即可: - -```js -export async function unmount() { - instance.$destroy(); -+ instance.$el.innerHTML = ""; //新增这一行代码 - instance = null; - router = null; -} -``` - -但是其实,来回切换子项目并不会使内存不断增加。也就是说,即使卸载子项目时,子项目占用的内存没有被释放,但是下次加载时会复用这块内存,那这样的话,子项目会不会加载更快?(还未考证) - -## 安全和性能的问题 - -qiankun 将每个子项目的 js/css 文件内容都记录在一个全局变量中,如果子项目过多,或者文件体积很大,可能会导致内存占用过多,导致页面卡顿。 - -另外,qiankun 运行子项目的 js,并不是通过 script 标签插入的,而是通过 eval 函数实现的,eval 函数的安全和性能是有一些争议的:[MDN的eval介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval) - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/source/_posts/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210.md" deleted file mode 100644 index 29c62b86d..000000000 --- "a/source/_posts/qiankun\347\232\204css\346\262\231\347\256\261\351\232\224\347\246\273\350\247\243\345\206\263\346\226\271\346\241\210.md" +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: qiankun的css样式污染解决方案 -categories: - - 微前端 -tags: - - 微前端 -keywords: 微前端 -description: qiankun的css样式污染解决方案 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201220000516.png' -date: 2020-12-19 23:57:26 ---- - -> 在使用qiankun框架做微前端开发的过程中,遇到了诸多难题,比如路由重定向、变量名及事件名冲突、挂载注销机制及生命周期、keep-alive等,目前遇到的比较难解决的问题可能就是css样式污染问题了。这次抽出了几天时间研究了一下,遂总结此文 - -# 问题概述 - -在qiankun加载子应用后,主子应用的样式之间会产生污染,常见的css样式污染有以下几种情况: -- 无论是否进行样式隔离:主应用的样式污染了子应用(原因:主应用的样式添加了 `!important` 属性或 `>>>` 穿透属性) -- 未进行样式隔离:子应用的样式污染了主应用(原因:样式重名,后加载的优先级高) -- 处理过样式隔离:子应用打开的弹窗、抽屉、popover等这种需要插入到主应用body的dom元素,样式丢失或应用了主项目的样式(原因:开启沙箱时,子应用的样式作用域只在子应用内,但如描述,子应用的dom被插入到了主应用的body中,遂出现了此种情况) - -# qiankun自带的css沙箱 - -个人理解,qiankun加载子项目css样式机制大体为:挂载子应用时将子应用的css样式以style标签的形式插入并做快照,卸载子应用时再将快照内的style样式删除。所以在加载子应用期间,若未开启css沙箱隔离,后加载的这些样式,可能会对整个系统的样式产生影响,对此,qiankun提供了两种css沙箱功能,可以将子应用的样式包裹在沙箱容器内部,以此来达到样式隔离的目的。大体代码如下所示: - -```js -this.microApp = loadMicroApp( - { apps infos ... }, - { - sandbox: { - strictStyleIsolation: true // 严格沙箱 - experimentalStyleIsolation: true // 实验性沙箱 - } - } -) -``` - -1. 严格沙箱 - -在加载子应用时,添加`strictStyleIsolation: true`属性,实现形式为将整个子应用放到`Shadow DOM`内进行嵌入,完全隔离了主子应用 - -缺点: -- 子应用的弹窗、抽屉、popover因找不到主应用的body会丢失,或跑到整个屏幕外(具体原因作者并未详细研究) -- 主应用不方便去修改子应用的样式 - -2. 实验性沙箱 - -在加载子应用时,添加`experimentalStyleIsolation: true`属性,实现形式类似于vue中style标签中的scoped属性,qiankun会自动为子应用所有的样式增加后缀标签,如:`div[data-qiankun-microName]` - -缺点: -- 子应用的弹窗、抽屉、popover因插入到了主应用的body,所以导致样式丢失或应用了主应用了样式 - -# 最终解决方案 - -说了这么多qiankun自带的css沙箱隔离,但都有各自的缺点,并且对于系统的实现上,影响范围还比较严重,代码的修改范围也比较大。作者的项目中,主子应用都是vue项目,并且都用了element家的样式且都各自魔改过,遂果断不开启css沙箱,给各自的项目class全局加上一个各自的命名空间,可以自己的项目名,比如:`myVue body`、`myVue el-form-info__label`、`myVue customClass`等。 - -# 代码实现 - -1. 添加依赖 - -```bash -→ npm i postcss-plugin-namespace -D -``` - -2. 配置postcss - -在项目根目录创建`postcss.config.js`文件,并复制以下内容: - -```js -// console.log('=> => => postcss.config.js start => => =>') -module.exports = (ctx) => { - return { - plugins: [ - require('postcss-plugin-namespace')('#lee_project', { - ignore: [ - 'html', /body/, 'span', 'el-form-item' - ] - }), - ] - } -} -// console.log('=> => => postcss.config.js end => => =>') -``` - -该插件会将全局所有class前加上统一前缀,并过滤掉ignore内的标签;ignore内可以写字符串,可以写正则表达式。但每次编译前都会运行,所以可能会增加编译时间,所以日常开发环境下可以将此文件名随便改成别的,上线前记得改回来调试一下(如果直接隐藏掉代码的话,只要有`postcss.config.js`这个文件webpack会自动帮你执行,并且会提示你的postcss啥也没干,也相当于每次都走了这个脚本)。 - -注意:如果用`/body/`这样的正则,会将所有带body的class都过滤掉,比如`el-drawer__body`、`el-dialog__body`等。 - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266.md" "b/source/_posts/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266.md" deleted file mode 100644 index 81a64b811..000000000 --- "a/source/_posts/viewerjs\346\237\245\347\234\213\345\244\247\345\233\276\347\273\204\344\273\266.md" +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: viewerjs查看大图组件 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: viewerjs查看大图组件 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210225140807.png' -date: 2021-02-06 23:07:33 ---- - -> 因为项目没有使用`element-ui`,所以不能使用自带的`el-image`组件进行大图查看,遂找了一个单独的组件 - -# 安装 - -```bash -npm install viewerjs -``` - -# 使用案例 - -```html - -
    - Picture -
    - - -
    -
      -
    • Picture 1
    • -
    • Picture 2
    • -
    • Picture 3
    • -
    -
    -``` - -```js -// see document: https://github.com/fengyuanchen/viewerjs/blob/master/README.md -import Viewer from 'viewerjs'; -import 'viewerjs/dist/viewer.css'; -let viewer: any; -export function initImageViewer(thumbnail: string = '') { - // 获取最新的消息框实例 - const msgDom: any = (document as any).getElementById('messageBox'); - // 已加载过 - if (viewer) { - // 更新实例里的图片源 - viewer.update(msgDom); - } else { // 第一次加载 - viewer = new Viewer(msgDom, { - // 只筛选出图片消息 - filter(image: any) { - const isImgMsg = image.className.indexOf('imageMsg') > -1; - return isImgMsg; - }, - // 去掉url中的缩略图参数 - url(image: any) { - return image.src.replace(thumbnail, ''); - }, - }); - } -} - -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220.md" "b/source/_posts/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220.md" deleted file mode 100644 index 7140c24ad..000000000 --- "a/source/_posts/vue3\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\351\222\251\345\255\220.md" +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: vue3的生命周期钩子 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: vue3的生命周期钩子 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201227213102.png' -date: 2020-12-27 21:28:58 ---- - -> vue3的生命周期钩子与vue2的差不多,只是命名和调用上有一些诧异,详见下文 - -# 与 2.x 版本生命周期相对应的 Composition API - -| Vue 2.x | Vue 3.x -|---|--- -| ~~beforeCreate~~ | 改用 setup() -| ~~created~~ | 改用 setup() -| beforeMount | onBeforeMount -| mounted | onMounted -| beforeUpdate | onBeforeUpdate -| updated | onUpdated -| beforeDestroy | onBeforeUnmount -| destroyed | onUnmounted -| errorCaptured | onErrorCaptured - - -# 新增的钩子函数 - -除了和 2.x 生命周期等效项之外,Composition API 还提供了以下调试钩子函数: - -- onRenderTracked -- onRenderTriggered - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213.md" "b/source/_posts/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213.md" deleted file mode 100644 index 358ddd711..000000000 --- "a/source/_posts/\343\200\212\345\211\215\347\253\257\345\206\205\345\217\202\343\200\213.md" +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: 《前端内参》读书笔记 -categories: - - 读书笔记 -tags: - - 读书笔记 -keywords: 读书笔记 -description: 读书笔记 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170225.png -date: 2020-06-23 21:08:31 ---- - -> 做web前端开发也有两年的时间了,但技术层面一直很浅,特别是近期感觉遇到了知识瓶颈,还是因为看书少、不爱总结。本次笔记在参考Bob Ma大佬整理分享的[《前端内参》](https://coffe1891.gitbook.io/frontend-hard-mode-interview/)的基础上,记录并整理下来一些自己平时不注意的知识点。 - - -# 壹.1.1.3 ES 8 新特性 - -## 字符串追加 - -在 ES 8 中String新增了两个实例函数`String.prototype.padStart`和`String.prototype.padEnd`,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。 -- String.padStart(targetLength,[padString]) - *targetLength*:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。 - *padString*:(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为空格。 -- `String.padEnd(targetLength,padString])` 参数释义同上。 - -```js -'es8'.padStart(2); // 'es8' -'es8'.padStart(5); // ' es8' -'es8'.padStart(6, '1891'); // '189es8' -'es8'.padStart(14, 'coffe'); // 'coffecoffeces8' -'es8'.padStart(7, '0'); // '0000es8' - -'es8'.padEnd(2); // 'es8' -'es8'.padEnd(5); // 'es8 ' -'es8'.padEnd(6, '1891'); // 'es81891' -'es8'.padEnd(14, 'coffe'); // 'es8coffecoffec' -'es8'.padEnd(7, '9'); // 'es89999' -``` - - -## 异步函数 -Async Functions也就是我们常说的`Async/Await`,相信大家对于这个概念都已经不陌生了。`Async/Await`是一种用于处理JS异步操作的语法糖,可以帮助我们摆脱`回调地狱(callback hell)`,编写更加优雅的代码。 - -通俗的理解,async关键字的作用是告诉编译器对于标定的函数要区别对待。当编译器遇到标定的函数中的await关键字时,要暂时停止运行,等到await标定的函数处理完毕后,再进行相应操作。如果该函数fulfiled了,则返回值是fulfillment value,否则得到的就是reject value。 - -下面通过拿普通的promise写法来对比,就很好理解了: - -```js -async function asyncFunc() { - const result = await otherAsyncFunc();// otherAsyncFunc()返回一个Promise对象 - console.log(result); -} - -// 等同于: -function asyncFunc() { - return otherAsyncFunc()// otherAsyncFunc()返回一个Promise对象 - .then(result => { - console.log(result); - }); -} -``` - -按顺序处理多个异步函数的时候优势更为明显: - -```js -async function asyncFunc() { - const result1 = await otherAsyncFunc1();// otherAsyncFunc1()返回一个Promise对象 - console.log(result1); - const result2 = await otherAsyncFunc2();// otherAsyncFunc2()返回一个Promise对象 - console.log(result2); -} - -// 等同于: -function asyncFunc() { - return otherAsyncFunc1()// otherAsyncFunc1()返回一个Promise对象 - .then(result1 => { - console.log(result1); - return otherAsyncFunc2();// otherAsyncFunc2()返回一个Promise对象 - }) - .then(result2 => { - console.log(result2); - }); -} -``` - -并行处理多个异步函数: - -```js -async function asyncFunc() { - const [result1, result2] = await Promise.all([ - otherAsyncFunc1(),// otherAsyncFunc1()返回一个Promise对象 - otherAsyncFunc2() // otherAsyncFunc2()返回一个Promise对象 - ]); - console.log(result1, result2); -} - -// 等同于: -function asyncFunc() { - return Promise.all([ - otherAsyncFunc1(),// otherAsyncFunc1()返回一个Promise对象 - otherAsyncFunc2() // otherAsyncFunc2()返回一个Promise对象 - ]) - .then([result1, result2] => { - console.log(result1, result2); - }); -} -``` - -处理错误: - -```js -async function asyncFunc() { - try { - await otherAsyncFunc();// otherAsyncFunc()返回一个Promise对象 - } catch (err) { - console.error(err); - } -} - -// 等同于: -function asyncFunc() { - return otherAsyncFunc()// otherAsyncFunc()返回一个Promise对象 - .catch(err => { - console.error(err); - }); -} -``` - -# 壹.1.1.4 ES 9 新特性 - -## 异步迭代器 Asynchronous Iteration - -在`async`/`await`的某些时刻,你可能尝试在同步循环中调用异步函数。例如: - -```js -async function func(array) { - for (let i of array) { - await someFunc(i); - } -} -``` - -这段代码不会达到预期目的,下面这段同样也不会: - -```js -async function func(array) { - array.forEach(async i => { - await someFunc(i); - }); -} -``` - -上面这段代码中,循环本身依旧保持同步,并在内部异步函数之前全部调用完成。 -引入异步迭代器后,就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。 - -```js -async function func(array) { - for await (let i of array) {//异步迭代 - someFunc(i); - } -} -``` - -更多详细论述见“壹.2.12”。 - -## 重新修订了字面量的转义 - -ES9 之前,`\u`表示unicode转义,`\x`表示十六进制转义,`\`后跟一个数字表示八进制转义,这使得创建特定的字符串变得不可能,例如Windows文件路径`C:\uuu\xxx\111`。 - -要取消转义序列的语法限制,可在模板字符串之前使用标记函数String.raw。 - -```js -let s = `\u{54}` //会转义成unicode "T" -console.log(s);//>> T - -let str = String.raw`\u{54}`; //不会被转义 -console.log(str);//>> \u{54} -``` - -## Rest / Spread 属性 - -这个就是我们通常所说的三个点`...`,在`=`左边的是**rest参数**,放在`=`右边或者作为参数的是**扩展运算符**,这项特性在ES6中已经引入,但是ES6中的作用对象仅限于数组。在ES9中,为对象提供了像数组一样的rest参数和扩展运算符: - -```js -const obj = { - a: 1, - b: 2, - c: 3 -}; -const { a, ...param } = obj; //这里...是rest参数 -console.log(a); //>> 1 -console.log(param); //>> {b: 2, c: 3} - -function foo({ a, ...param }) {//这里...是扩展运算符 - console.log(a); //>> 1 - console.log(param); //>> {b: 2, c: 3} -} -``` - -## 正则表达式dotAll模式 - -正则表达式中点`.`匹配除回车外的任何单字符,标记`s`改变这种行为,允许匹配回车换行。 - -```js -/hello.world/.test('hello\nworld'); // false -/hello.world/s.test('hello\nworld'); // true -console.log(/hello.world/s.test(`hello -world`)) //>> true -``` - -## 正则表达式命名捕获组 - -未看 - -## 正则表达式后行断言 - -未看 - -## 正则表达式 Unicode 转义 - -未看 - - -# 壹.1.1.5 ES 10 新特性 - -## 啊 - - -# 祝君无Bug~ - - - - - - diff --git "a/source/_posts/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts.md" "b/source/_posts/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts.md" deleted file mode 100644 index 396b8243d..000000000 --- "a/source/_posts/\344\270\255\345\244\256\344\272\213\344\273\266\346\200\273\347\272\277\346\217\222\344\273\266vue-bus-ts.md" +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: 中央事件总线插件vue-bus-ts -categories: - - Vue -tags: - - Vue -keywords: vue插件,事件总线 -description: 描述 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111165942.png -date: 2020-11-07 18:28:46 ---- - -> 项目中难免会遇到非父子组件之间的传参与通信问题,遂整理此文。 - -# 简介 -[vue-bus-ts](https://github.com/wowill/vue-event-bus) 是一款支持在ts环境下使用的全局事件总线插件。 - -# 安装及使用方法 - -## 安装 - -```zsh -npm i -S vue-bus-ts -``` - -## 注册 - -安装后需要在main.ts中引入并注册,挂载到全局的Vue实例上即可 - -```js -# main.ts - -import Vue from 'vue'; -import EventBus from 'vue-bus-ts'; - -Vue.use(EventBus); -var bus = new EventBus.Bus(); - -new Vue({ - bus, - render: (h) => h(App), -}).$mount('#app'); -``` - -## 注册事件 - -在调用事件前,需要实现注册该事件,否则不会生效 - -一般写在vue文件的mounted或者created生命周期中 - -```js -# *.vue - -var eventId = this.$bus.$on('event_name', function (params: any) { - // params is the parameter passed in by $emit - // do something... -}) -``` - -## 调用事件 - -注册成功后,打印`this.$bus`即可看到当前的事件总线实例,下面为调用的方法 - -```js -# *.vue -// params can pass in any form of value, including Array, Object, String, Number, null, undefined or even array expansion items.or example, - -var eventResult = this.$bus.$emit('event_name', params) - -1. var result = this.$bus.$emit('event_name', [1,2,3]) -2. var result = this.$bus.$emit('event_name', {}) -3. var result = this.$bus.$emit('event_name', 'string') -4. let a = 1, b = 'test', c = [1, 2, 3], d = {a: 'test'} - var result = this.$bus.$emit('event_name', a, b ,c, d) -5. var result = this.$bus.$emit('event_name', null) -6. var result = this.$bus.$emit('event_name') -``` - -## 注销事件 - -如果不注销的话,下一次`$on`注册同一个事件时,会生成两个相同的事件,调用时会触发n次。所以建议在离开当前页面或当前模块时注销该事件,即写在`beforeRouterLeave`中(如果开启了keep-alive,可写在deactivated中)。 - -```js -# *.vue - -this.$bus.$off('event_name', eventId) // To unbind event binding, eventId is the return value of this.$bus.$on -``` - -## 订阅事件? - -```js -# *.vue - -let result = this.$bus.$subscribed('event_name') -if (result) { - // do something... -} -``` - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242.md" "b/source/_posts/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242.md" deleted file mode 100644 index 71168bf35..000000000 --- "a/source/_posts/\344\273\2160\345\274\200\345\247\213\346\220\255\345\273\272Hexo\344\270\252\344\272\272\345\215\232\345\256\242.md" +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: 从0开始搭建Hexo个人博客 -categories: - - 个人文档 -tags: - - 个人文档 -keywords: '前端,Hexo,个人文档' -description: 个人文档 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170432.png -date: 2020-06-07 23:23:55 ---- - -> 搭建个人博客是每个程序员成长的必经之路,不但可以记录与分享自己在学习过程中Get到的新技能、新知识,还能顺便提高一下自己的文采。 - -# Hexo简介 -Hexo是一款基于Node.js的静态博客框架,可方便快捷的托管于GitHub上,是搭建博客的首选框架。 - -根据[Hexo官网](https://hexo.io/zh-cn/)介绍,主要有以下四大优点: -- 超快速度: Node.js 所带来的超快生成速度,让上百个页面在几秒内瞬间完成渲染。 -- 支持 Markdown:Hexo 支持 GitHub Flavored Markdown 的所有功能,甚至可以整合 Octopress 的大多数插件。 -- 一键部署:只需一条指令即可部署到 GitHub Pages, Heroku 或其他平台。 -- 插件和可扩展性:强大的 API 带来无限的可能,与数种模板引擎(EJS,Pug,Nunjucks)和工具(Babel,PostCSS,Less/Sass)轻易集成 - - -# Hexo搭建步骤 - -## 安装Git - -Git是目前世界上最先进的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。无论是个人代码管理还是团队合作开发中,学会git那都是百利而无一害的。如果对git还不是很了解,推荐去[廖雪峰老师的博客](https://www.liaoxuefeng.com/wiki/896043488029600)或者先看一下[Git Book](https://git-scm.com/book/zh/v2)的前三章。 - -```bash -# 安装命令 -$ sudo apt-get install git - -# 查看版本 -$ git --version -``` - -## 安装Node.js - -Hexo是基于Node.js环境运行的,所以需要安装Node环境及npm包管理工具。 - -```bash -# node安装命令 -$ sudo apt-get install nodejs - -# 查看node版本 -$ node -v - -# npm安装命令 -$ sudo apt-get install npm - -# 查看npm版本 -$ npm -v -``` - -## 安装Hexo - -```bash -# 利用npm全局安装hexo脚手架 -$ npm install -g hexo-cli - -# 查看hexo版本 -$ hexo -v - -# 删除hexo -$ npm uninstall -g hexo-cli - -# 查看npm全局版本 -$ npm ls -g --depth=0 -``` - -## 创建博客项目 - -到此为止,装好了node环境以及hexo框架,基本上前期的环境配置就完成了,接下来就可以创建自己的博客项目了。 - -```bash -# 新建一个文件夹,如名为blog -$ mkdir blog - -# 进入blog文件夹 -$ cd blog - -# 初始化hexo -$ hexo init -``` - -初始化成功后,blog文件夹下会出现如下文件: -- _config.yml: 博客的核心配置文件(设置主体、标题等属性) -- package.json:项目所需的依赖包 -- source:用来存放你的文章 -- themes:放下下载的主题 -- public:存放生成的页面 -- scaffolds:生成文章的一些模板 - -```bash -# 安装所需依赖 -$ npm install -``` - -安装成功后,会出现node_modules文件夹,文件夹内即安装的package.json内所有依赖包。接下来就可以配置并启动hexo了 - -```bash -# 清除缓存文件 (db.json) 和已生成的静态文件 (public) -$ hexo clean - -# 生成静态文件,generate -$ hexo g - -# 部署博客网站,deploy -$ hexo d - -# 启动服务器,server -$ hexo s -g -``` - -运行成功后,浏览器打开`http://localhost:4000`便可看到你的hexo博客项目了,除了主题有点儿吃藕,还是挺不错的~ - -## 将Hexo部署到GitHub - -这一步,我们就可以将hexo和GitHub关联起来,也就是将hexo生成的文章部署到GitHub上,打开站点配置文件_config.yml,翻到最后,修改为下面这样,其中LeeDebug改为你的GitHub账户名 -```bash -deploy: - type: git - repo: git@github.com:LeeDebug/LeeDebug.github.io.git - branch: master -``` - -这个时候需要先安装deploy-git,也就是部署的命令,这样你才能用命令部署到GitHub。 - -```bash -$ npm install hexo-deployer-git --save -``` - -部署项目 -```bash -$ hexo clean -$ hexo g -$ hexo d -``` - -部署成功后,浏览器打开`http://LeeDebug.github.io`,就能看到你的博客了 - - -# 安装主题 - -首先下载主题包,如[butterfly](https://github.com/jerryc127/hexo-theme-butterfly) -```bash -npm i hexo-theme-butterfly -``` - -配置`_config.yml`文件 -```bash -theme: butterfly -``` - -# 新建文章 - -首先修改`/scaffolds/post.md`文件模板,改为想要的形式,比如 -```bash ---- -title: {{ title }} -tags: -- Hexo -categorier: -- Hexo -keywords: "Hexo,笔记" -date: {{ date }} -description: "描述" -cover: https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg.png ---- -``` - -利用post模板新建文章 -```bash -hexo new post 文章标题 -``` - -随后在`source/_posts/`文件夹下会出现`文章标题`的文件夹和`文章标题.md`的MarkDown文件,文章内容在`*.md`文件内编辑即可 - - -# 新增分类页 - -```bash -hexo new page categories -``` - -将`/source/categories/index.md/`这个文件改为以下内容 -```bash ---- -title: 分类 -date: 2018-01-05 00:00:00 -type: "categories" ---- -``` - - -# 新增标签页 - -```bash -hexo new page tags -``` - -将`/source/tags/index.md/`这个文件改为以下内容 -```bash ---- -title: 标签 -date: 2018-01-05 00:00:00 -type: "tags" ---- -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211.md" "b/source/_posts/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211.md" deleted file mode 100644 index 89a21a1b5..000000000 --- "a/source/_posts/\344\275\240\344\270\215\347\237\245\351\201\223\347\232\204JavaScript\357\274\210\344\270\212\345\215\267\357\274\211.md" +++ /dev/null @@ -1,315 +0,0 @@ ---- -title: 你不知道的JavaScript(上卷) -categories: - - 读书笔记 -tags: - - 读书笔记 -keywords: 读书笔记 -description: 读书笔记 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170127.png -date: 2020-07-18 23:23:55 ---- - -> 去年就想读这一套书,由于个人原因一直拖到现在,今天终于周末在家没事,看了5个小时才看了上卷的1/3(其实也就60页)。主要讲述了作用域和闭包的相关知识,可能是由于自己基础知识的匮乏,所以进度有些慢。总之,要提高阅读速度了。 - -# 作用域和闭包 - -## 作用域是什么 - -- JavaScript是一门编译语言,其引擎进行编译的步骤和传统的编译语言非常相似,包括下列三个编译步骤: - - 分析/词法分析(Tokenizing/Lexing)(词法化、单词化) - - 解析/语法分析(Parsing) - - 代码生成 - - -- 作用域是一种规则,用于确定在何处以及如何查找该变量(标识符),即用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找 - - 引擎 - - 编译器 - - 作用域 - - -- var a = 2; - - var a:编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。(定义声明在编译阶段进行) - - a = 2:编译器会为引擎生成运行时所需要的代码,这些代码被用来处理a=2这个赋值操作。首先会询问作用域,在当前作用域集合中是否存在一个叫作a的变量。如果是,引擎就会使用这个变量;否则会向外层嵌套的作用域继续查找该变量。(赋值声明会被留在原地等待执行阶段被调用) - - -- 编译器在编译过程中,对变量有两种查询方式 - - LHS查询:变量在左侧,目的为赋值操作 - - RHS查询:变量在非左侧,目的为获取变量的值 - - -- LHS和RHS都会再当前执行作用域中开始 - - 不成功的RHS会导致抛出ReferenceError异常 - - 不成功的LSH会导致自动隐式的创建一个全局变量(非严格模式下),该变量使用LHS查询的目标作为标识符,或者抛出ReferenceError异常(严格模式下) - - -- LHS和RHS的异常错误 - - ReferenceError异常:同作用域判别失败相关 - - TypeError异常:作用域判别成功,但对结果的操作是非法或不合理的 - - -- 编译器可以再代码生成的同时处理声明和值的定义 - - -- 在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量或抵达最外层作用域(即全局作用域)为止 - - -## 词法作用域 - -- 作用域的两种工作模型 - - 词法作用域:在写代码或定义时确定的,作用域链基于作用域的嵌套,即更关注在何处声明。大多数编程语言都采用 - - 动态作用域:在运行时确定的,作用域链是基于调用栈的,即更关注函数是从何处调用的。this的机制也是如此 - - -- 词法作用域:是一套关于引擎如何寻找以及会在何处找到变量的规则 - - -- JavaScript中有两个机制可以“欺骗”词法作用域。但在编译时引擎均无法对作用域查找进行优化,所以不要使用它们 - - eval(...):生成代码并运行 - - with:会产生内存泄漏 - - -- 箭头函数:ES6添加了一个特殊的语法形式用于函数声明,将this同词法作用域联系起来 - - -- 箭头函数在涉及this绑定时的行为和普通函数的行为完全不一致。它放弃了所有普通this绑定的规则,取而代之的是用当前的词法作用域覆盖了this本来的值 - - -## 函数作用域和块作用域 - -- 函数作用域:属于这个函数的全部变量都可以再整个函数的范围内使用及复用(事实上在嵌套的作用域内也可以使用) - - -- 基于作用域的隐藏方法:大豆是从最小特权原则(最小授权原则、最小暴露原则)中引申出来的,即应该最小限度的暴露必要内容 - - -- 如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式 - - -- 函数声明和函数表达式之间最重要的区别:他们的名称标识符将会绑定在何处 - - -- 函数表达式的应用场景: - - 匿名函数表达式:`setTimeout(() => {}, 0)` - - 立即执行函数表达式-IIFE(Immediately Invoked Function Expression):`(function foo(){...})()`或`(function(){...}())` - - -- 匿名函数表达式的缺点:(==养成始终给函数表达式命名的好习惯==) - - 在栈追踪中不会显示出有意义的函数名,使调试很困难 - - 没有函数名,自身引用自身,只能使用过期的`arguments.callee`来引用 - - 可读性差,可理解性差 - - -- 除了JavaScript歪的很多编程语言都支持块作用域(表面上看没有,除非更加深入的研究)P30页 - - -- 块作用域的应用场景: - - with:从对象中创建出的作用域仅在with声明中而非外部作用域中有效 - - try/catch:ES3规范中规定catch分句会创建一个块作用域 - - let:为其声明的变量隐式的创建了一个块作用域 - - const:创建块作用域变量,但值是固定的常亮 - - ??? - - -- ==for循环的let i==: - - 将i重新绑定到循环的每一个迭代中,并确保使用上一个循环迭代结束时的值重新进行赋值 - - i在循环过程中不止被声明一次,每次迭代都会被声明,随后的每次迭代都会使用上一个迭代结束时的值来初始化下一个i - - -- 为变量显式声明块作用域,并对变量进行本地绑定是非常有用的==工具==,P34页 - - -## 提升 - -- 任何声明在某个作用域的变量,都将属于这个作用域 - - -- 包括变量和函数在内的所有声明都会再任何代码被执行前首先被处理,可以将这个过程形象的想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端 - - -- 先有声明,后有赋值 - - -- 在提升中,函数声明会首先被提升,然后才是变量声明;但出现在后面的函数声明还是可以覆盖前面的 - - -## 作用域闭包 - -- 闭包是基于词法作用域书写代码时所产生的自然结果 - - -- 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行 - - -- 无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包 - - -- 闭包的应用场景: - - function调用`setTimeout(...)` - - 定时器、事件监听器、Ajax请求、跨窗口通信、WebWorkers、任何异步或同步任务,只要使用了==回调函数==,实际上就是在使用闭包 - - for循环 - - 立即执行函数表达式IIFE:`(function(){...})())`(本身创建了闭包,但严格来说并不是闭包) - - 模块模式:比如jQuery和$符就是jQuery模块的公共api - - -- 延迟函数的回调会在循环结束时才执行,即使定时器是`setTimeout(..., 0)` - - -- 在迭代内使用IIFE会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问 - - -- let声明,可以用来劫持块作用域,并且在这个块作用域中声明一个变量;本质上这是将一个块转换成一个可以被关闭的作用域 - - -- 最常见的实现模块模式的方法被称为模块暴露 - - -- 模块模式需要具备的两个必要条件: - - 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例) - - 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有属性 - - -- 模块的两个主要特征: - - 为创建内部作用域而调用了一个包装函数 - - 包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包 - - -- 模块也是普通函数,也可以接受参数 - - -- 模块模式另一个简单而强大的变化用法是,命名将要作为公共api返回的对象(大多数模块依赖加载器/管理器的本质都是将这种模块定义封装进一个友好的api) - - -- import:将一个模块中的一个或多个api导入到当前作用域,并可以分别绑定在不同的变量上 -- module:将整个模块的api导入并绑定 到一个变量上 -- export:将当前模块的一个标识符(变量或函数)导出为公共api - - - -# this和对象原型 - -> 任何足够先进的技术都和魔法无异。 - -> 在遇到问题时,许多开发者并不会深入思考为什么this的行为和预期的不一致,也不会试图回答那些很难解决但确实非常重要的问题,他们只会回避这个问题并使用其他方法来达到目的,这显然不是一种很好的解决办法。 - -## 关于this - -- this关键字是JavaScript中最复杂的机制之一 - -- this指向的两大误区: - - 指向函数自身 - - 指向函数的作用域(在任何情况下this都不会指向函数的词法作用域,因为作用域“对象”是存在于JavaScript的引擎内部的) - -- 当你想把this和词法作用域的查找混合使用时,这是无法实现的! - -- 不能使用this来引用一个词法作用域内部的属性 - -- this实际上是在函数被调用时发生的绑定,this指向什么只取决于函数在哪被调用,和函数的生命位置毫无关系。 - - -## this全面解析 - -- 要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置,最重要的就是分析调用栈(为了到达当前执行位置所调用的所有函数,即函数调用链)。 - -- 调用位置如何决定this的绑定对象?可顺序应用下面四条规则来判断 - - 是否在new中调用(new绑定)?绑定到新创建的对象。`var bar = new foo()` - - 是否通过call、apply(显示绑定)或者bind(硬绑定)调用?绑定到指定的对象。`var bar = foo.(obj2)` - - 是否在某个上下文对象(隐式绑定)中调用?绑定到那个上下文对象。`var bar = obj1.foo()` - - 如果都不是即独立函数调用(默认绑定),严格模式下绑定到undefined,非严格模式下绑定到全局对象。`var bar = foo()` - -- 默认绑定:直接使用不带任何修饰的函数引用进行调用 -```js -function foo() { - console.log(this.a) -} -var obj = { - a: 2, - foo: foo -} -var bar = obj.foo // 函数别名 -var a = 'oops, global' // a是全局对象的属性 -bar() // >> 'oops, global' - -// 虽然bar是obj.foo的一个引用,看似是隐式绑定,但实际上,它引用的是foo的函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。 -``` - -- 隐式绑定:调用位置是否有上下文对象,或者说是否被某个对象拥有或包含。即我们必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接(隐式)绑定到这个对象上。 -```js -// 参数传递就是一种隐式赋值 -function foo() { - console.log(this.a) -} -function doFoo(fn) { - fn() // fn其实引用的是foo函数本身 -} -var obj = { - a: 2, - foo: foo -} -var a = 'oops, global' // a是全局对象的属性 -doFoo(obj.foo) // >> 'oops, global' - -// 如果把函数传入语言内置的函数而不是自定义的函数,结果是一样的 -function foo() { - console.log(this.a) -} -var obj = { - a: 2, - foo: foo -} -var a = 'oops, global' // a是全局对象的属性 -setTimeout(obj.foo, 100) // >> 'oops, global' - -// JavaScript环境中内置的setTimeout()函数实现和下面伪代码类似 -function setTimeout(fn, delay) { - // 等待delay秒 - fn() // 调用函数内容本身 -} -``` - -- 显示绑定:在对象内部不包含函数引用的情况下,在某个对象上强制调用函数,可以使用`call(...)`和`apply(...)`方法,它们的第一个参数是一个对象,它们会吧这个对象绑定到this上,接着在调用函数时指定这this。 -```js -function foo() { - console.log(this.a) -} -var obj = { - a: 2 -} -foo.call(obj) // >> 2 -``` - -- new绑定:JavaScript中的new机制实际上和面向类的语言完全不同,JavaScript中的new构造函数,其实只是一些使用new操作符调用的普通函数。它们不会属于某个类,也不会实例化一个类。 - -- 有些调用可能在无意中使用默认绑定规则。为了保护全局对象,可以使用一个`DMZ对象(DemilitarizedZone,非军事区)`,即一个空的非委托的对象,比如`const ∅ = Object.create(null)`,使用变量名`∅`不仅让函数变得更加安全,而且可以提高代码的可读性,因为`∅`标识“我希望this是空”这比null的含义更清楚。`Object.create(null)`和`{}`很像,但并不会创建`Object.prototype`这个委托,所以它比{}“更空”。 - -- ES6中`箭头函数()=>{}`并不会使用这四条标准的绑定规则,而是根据当前的`词法作用域`来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。这其实和ES6之前的`self = this`机制一样。 - -- 代码风格: - - 词法作用域风格:只使用`词法作用域`,并完全抛弃错误的this风格。 - - this风格:完全采用this风格,必要时使用`bind(...)`,尽量避免使用`self = this`和`箭头函数()=>{}`。 - - -## 对象 - - -## 混合对象“类” - - -## 原型 - - -## 行为委托 - - - - -# 祝君无Bug~ - - - - - - - - - diff --git "a/source/_posts/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256.md" "b/source/_posts/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256.md" deleted file mode 100644 index 90bcd32f6..000000000 --- "a/source/_posts/\344\277\256\346\224\271element-ui\347\232\204\345\205\250\345\261\200\351\205\215\347\275\256.md" +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: 修改element-ui的全局配置 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 修改element-ui的全局配置 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210225125906.png' -date: 2021-01-26 23:44:41 ---- - -> 项目因使用qiankun嵌入了子项目,且都使用了`element-ui`的弹窗样式,遂导致子应用中插入到父应用body的弹窗因层级过低无法展示 - -# 配置信息 - -因`element-ui`的弹窗默的`z-index层级`默认是从`2000`开始的,并且打开多个弹窗时`z-index`会逐步递增,所以若主子应用不做区分的话,很可能会导致有的弹窗被遮挡 - -好在element提供了入口可将全局的弹窗层级设置一个起始值,即只需将子应用的层级初始值调高即可 - -```js -// 在main.ts中 -import Vue from 'vue'; -import Element from 'element-ui'; -Vue.use(Element, { - size: 'small', // 组件的默认尺寸 mini、small、medium - zIndex: 3000 // 弹窗默认层级初始值,默认2000 -}); -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203.md" "b/source/_posts/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203.md" deleted file mode 100644 index 8da0f8d26..000000000 --- "a/source/_posts/\345\211\215\347\253\257\344\270\252\344\272\272\345\255\246\344\271\240\347\254\224\350\256\260\344\270\216\351\241\271\347\233\256\350\247\204\350\214\203.md" +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: 前端个人学习笔记与项目规范 -categories: - - 个人文档 -tags: - - 个人文档 -keywords: 个人文档 -description: 个人文档 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170242.png -date: 2020-06-20 10:35:22 ---- - -``` -# Lee 更新于 2019-05-30 - -以下是学习Vue的渐进路线,并非适合所有人但适合大多数人 -如果是计算机专业的话,而且有其他语言基础的,1~2天就可以过一遍 -如果是非计算机专业的话,可能要半个月左右,不会的一定要勤问(发扬不要脸的精神) -看完之后别逗留太久,尽早跟着项目走,真正开始动手写点儿东西的时候,才是你刚开始入门的时候 - - - - 人生、工作的结果 = 思维方式 × 热情 × 能力 - - --- 稻盛和夫《活法》 -``` - - -# Basic Document: - -- HTML(***):http://www.w3school.com.cn/html/index.asp -- JS(****):https://wangdoc.com/javascript/index.html -- Js(W3c版):http://www.w3school.com.cn/jsref/jsref_obj_array.asp -- CSS3(*):http://www.w3school.com.cn/css3/index.asp -- Vue官方文档(重点)(*****):https://cn.vuejs.org/ -- VueX官方文档(*****):https://vuex.vuejs.org/zh/guide/ -- VueRouter官方文档(*****):https://router.vuejs.org/guide/#html -- Element UI框架(****):http://element.eleme.io/#/zh-CN (随用随找) -- 看Git的前3章节(***):https://git-scm.com/book/en/v2 -- ES6(*):http://es6.ruanyifeng.com/#README -- 阮一峰 Webpack Demo:https://www.jianshu.com/p/080e18fcf0e3 -- Http协议(**):https://www.cnblogs.com/ranyonsue/p/5984001.html -- 【Vue官方项目Demo】:https://panjiachen.github.io/vue-element-admin/#/login -- 【项目目录解析】(只看第一章就行):https://segmentfault.com/a/1190000009275424 - -# Basis SoftWare: - -**baiduCloud链接(Mac版):链接:https://pan.baidu.com/s/1dQJAJ8nSV2FyOcZt19KP5Q 密码:x605** - -**baiduCloud链接(Windows版):链接:https://pan.baidu.com/s/1Imq9YggdknZOegJc8LNr8A 密码:qo6z** - -- IDE:Websotrm -- GIt仓库管理:Sourcetree(用baiduCloud里的sourcetree276a.zip) -- 接口测试:Postman -- 浏览器(推荐):chrome -- 常用fq:Shadow--socks (敏感词,下载的时候把--去掉) - - -# 开会内容:新项目的统一规范(2019-02-22) - -## 1. Form封装问题 -``` -能在一个页面中写完的内容就不要随便抽象出一个组件,意义不大。 -``` - -## 2. 组件引用的路径: -``` -为了方便代码复用,统一使用绝对路径(如:'@/components/LqPagination'),按住Commond键 + 左键单击 即可跳转到该组件 -PS:如果你的webStorm绝对路径无法跳转,请根据以下操作引入webpack文件: - webStorm --> preferences --> Languages & Frameworks --> JavaScript --> Webpack:../qf_admin/admin/build/webpack.base.conf.js -``` - -## 3. 分页组件: -``` -为了解决分页时的loading问题,给分页组件传参数 -LqPagination组件:【:init-func="initData"】 -Pagination组件:【@pagination="initData"】 -每个函数只做一件事情,initData()就只是一个初始化列表函数,不要写别的内容。 -![image](http://note.youdao.com/yws/res/7075/13C1D152E20F48649D28553B9D5B309A) -``` - -## 4. 混入 -``` -每个模块的混入文件有且仅有一个,放在该模块主目录下的mixin里即可。 -混入文件内仅存放Vuex的相关代码,不要放其他的 -``` - -## 5. Vuex的使用 -``` -数据缓存的问题,以动态控制keep-alive来解决 -【能不用Vuex的地方就不要用】 -码表必须要用Vuex维护,并且必传:config = { usingCache: true } -![image](http://note.youdao.com/yws/res/7086/3C16AC303C164E6A8B4D22C81C0332FC) -``` - -## 6. 命名规范 & 页面规范 - -### 项目目录为: - - views // 项目文件目录 */ - - target-action // 日程模块 */ - - components // 此模块公用的组件 */ - - scheduleTable.vue - - mixin // 此模块公用的混入文件 */ - - Data.js - - index.vue // 模块主页 */ - - personal // 子模块 */ - - month.vue - - create.vue // 新增 || 修改 页面 */ - - announcement // 公告栏模块 */ - - ... // 其他模块 */ - -``` -# 文件夹 & 文件 命名规范 -全部为小写,多个单词的话中间以 _ 【下划线】连接,如: -target_action,day_schedule,customer_info - -# vue文件内部name命名规范 -***.vue页面的name(同其在路由中的name),按照此文件的目录结构【小驼峰】命名,如: -1. 日程模块主页的name命名为:targetActionIndex -2. 日程模块新增页面的name命名为:createTargetAction -3. target-action的子模块personal的month页面的name命名为:targetActionPersonalMonth - -# 函数命名规范 -按照函数的具体功能【小驼峰】命名,如: -1. 初始化数据函数:initData() -2. 刷新table函数:handleFilter() -3. 选中当前行函数:handleCurrentChange() -4. 提交 || 确认 函数:handleConfirm() - -# 页面格式规范 -整体section内:class="app-container"(即padding="20px";) -如xxc_admin项目所有页面都封装在el-card中,已自带padding:20px,所以就不需要class="app-container"了 -常规左右分栏布局:元素多的一侧 :span=14,少的一侧 :span=10 -页面最下方距离底部尽量留出50px~60px的距离,以免页面宽度缩小之后有的元素被换行而挡住 -高度实在放不开的页面,设置滚动:height: 800px;overflow:auto;overflow-x:hidden; - -# 按钮样式规范 -【table内】的按钮:放到el-tooltip中,提示框在上面,按钮size="mini",区分type,非圆角,带icon -【查询参数后】的按钮:round,正常sizi,区分type,带icon -【增加/修改页面】的按钮:非圆角,正常大小 - 「左边是“提交”,type="success" icon="el-icon-check"」 - 「右边是“作废”,type="danger" 」 -【详情页面】的按钮:“返回”放到最右边,type="warning" icon="el-icon-back" @click="gotoAndClose()" -1. 增加:type=success,icon=plus -2. 删除:type=danger,icon=delete -3. 修改:type=primary,icon=edit(如果有两种修改按钮时:icon=edit-outline) -4. 查询:type=default,icon=search -5. 刷新:type=default,icon=refresh -6. 确定:type=success,icon=success -7. 取消:type=warning,icon=error -8. 提交:type=success,icon=check -9. 关闭:type=warning,icon=close -10. 详情:type=info,icon=info -11. 打印:type=info,icon=printer -12. Excel:type=success,icon=document - -# 全局table样式规范 -1. 按照客户的需求,全局修改css -2. 如果右侧有操作列,需fixed="right"固定在最右侧,且设置固定宽度 -3. 能设置固定宽度的尽量设定,如:时间、日期、姓名(不会超过5个字) -4. 序号列,可有可无,如果表格字段过少,可充数用 - -# 函数执行成功后的提示 -1. 创建、修改、更新、删除【成功】后统一用this.$notify,即notification -2. 【操作错误、参数错误】等提醒用this.$message -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256.md" "b/source/_posts/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256.md" deleted file mode 100644 index b9378b0dd..000000000 --- "a/source/_posts/\345\212\240\350\275\275\350\201\212\345\244\251\345\216\206\345\217\262\350\256\260\345\275\225\345\271\266\344\277\235\347\225\231\346\273\232\345\212\250\346\235\241\345\275\223\345\211\215\344\275\215\347\275\256.md" +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: 加载聊天历史记录并保留滚动条当前位置 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 加载聊天历史记录并保留滚动条当前位置 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210413164038.png' -date: 2021-03-08 12:35:19 ---- - -> 在聊天框中,加载历史消息肯定是往消息的上面去加载,即对应数组的`Array.unshift()`操作,此时默认滚动条会回到顶部,所以我们需要重置滚动条的位置 - -# Vue中代码实现 - -首先记录加载历史前,滚动条的位置`scrollHeight` - -```js -const scrollHeight: number = (document as any).getElementById('messageBox').scrollHeight; -``` - -加载历史消息 - -```js -for (let i = 0; i < outMsg.list.length; i++) { - // todo sth, ex: 消息处理 - activeList.unshift(list[i]); -} -``` - -滚动到原来的位置 - -注:因为我是在vue环境下,需要确保页面已经渲染完,再滚动到加载前的位置,所以使用`Vue.nextTick()` - -```js -nextTick(() => { - const scrollDom: any = (document as any).getElementById('messageBox'); - scrollDom.scrollTop = (scrollDom.scrollHeight - scrollHeight); -}); -``` - -# jQuery代码实现 - -原理同上,直接上代码 - -```js -var scrollHeight = document.getElementById("messageBox").scrollHeight; -$("#messageBox").scrollTop(document.getElementById("messageBox").scrollHeight - scrollHeight); -``` - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250.md" "b/source/_posts/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250.md" deleted file mode 100644 index 6d8c2d45f..000000000 --- "a/source/_posts/\345\256\211\345\215\223\345\276\256\344\277\241\346\265\217\350\247\210\345\231\250\344\270\255type-file\347\232\204input\346\241\206\346\227\240\346\263\225\344\275\277\347\224\250.md" +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: 安卓微信浏览器中type=file的input框无法使用 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 安卓微信浏览器中type=file的input框无法使用 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210225125447.png' -date: 2021-01-17 19:35:03 ---- - -> 如果使用html的input框设为`type=file`来选择文件,在安卓的微信中打开页面时,会提示`暂无可使用应用`等错误提示 - -# 解决方案 - -若要选择图片,需将input框的accept属性设为`image/*`;若要选择文件,需将此属性设为` `空 - -```html - -``` -```js -const acceptFileTypeArr: any = computed(() => { // 当前支持上传类型 - if (uploadType.value === 'image') { - return 'image/*'; - } - if (uploadType.value === 'file') { - return ''; - } - return ''; -}); -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217.md" "b/source/_posts/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217.md" deleted file mode 100644 index 500368df5..000000000 --- "a/source/_posts/\345\256\236\347\216\260\350\276\223\345\205\245\346\241\206\347\232\204n\347\247\215\345\275\242\345\274\217.md" +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: 实现输入框的n种形式 -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 实现输入框的n种形式 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210226125630.png' -date: 2021-02-10 22:19:34 ---- - -> 开发过程中遇到的新方案 - -# textarea - -最常见的就是这种形式,不多赘述。代码如下 - -但要展示带html元素的内容,可能就不是那么方便了 - -```html - -``` - -# pre标签 - -`contenteditable`属性会将该段落变为可编辑状态,如同直接用vue的`v-html`展示。代码如下 - -```html -
    -  可以直接在这里输入.....
    -
    -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260.md" "b/source/_posts/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260.md" deleted file mode 100644 index 2b9465b41..000000000 --- "a/source/_posts/\346\237\245\347\234\213\344\273\243\347\240\201\350\241\214\346\225\260.md" +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: 查看代码行数 -categories: - - 个人文档 -tags: - - 个人文档 -keywords: 个人文档 -description: 查看代码行数 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210305125935.png' -date: 2021-03-01 12:51:35 ---- - -> 项目写久了,就像看看自己一个项目的真实代码有多少行,所以找了下面两个方法 - -# 命令行查看 - -用mac终端自带的的find命令,可以查看目录下每个文件的行数,及最后输出总行数 - -```bash -# input > -find . "(" -name "*.vue" -or -name "*.html" -or -name "*.ts" -or -name "*.js" ")" -print | xargs wc -l - -# output > -423 a.js -1842 b.vue -52 c.html -7253 total -``` - - -# VsCode查看 - -直接在vscode的全局搜索中(快捷键为Command + Shift + c),输入`b*[^:b#/]+.*$`,并使用`Use Regular Expression`选项(即最右侧的星号和方块的按钮),进行搜索,即可查看 - -```bash -# input > -b*[^:b#/]+.*$ - -# output > -7253 results in 77 files - -423 a.js -1842 b.vue -52 c.html -``` - -![VsCode查看代码行数](https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210305125911.png) - - -# 祝君无Bug~ \ No newline at end of file diff --git "a/source/_posts/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206.md" "b/source/_posts/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206.md" deleted file mode 100644 index 86ac1417e..000000000 --- "a/source/_posts/\347\234\213\345\256\214\350\256\251\344\275\240\345\275\273\345\272\225\346\220\236\346\207\202Websocket\345\216\237\347\220\206.md" +++ /dev/null @@ -1,239 +0,0 @@ ---- -title: 【转】看完让你彻底搞懂Websocket原理 -categories: - - Socket -tags: - - Socket -keywords: Socket,WebSocket -description: 看完让你彻底搞懂Websocket原理 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210421181317.png' -date: 2021-03-17 17:27:47 ---- - -> 最近在学习websocket的时候,从知乎的《WebSocket 是什么原理?为什么可以实现持久连接?》问题下看到了这个回帖,对我的帮助比较大,遂转载到自己的博客 - -# WebSocket 和 Http - -WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解 - -![WebSocket和Http的关系](https://pic1.zhimg.com/50/6651f2f811ec133b0e6d7e6d0e194b4c_hd.jpg?source=1940ef5c) - -有交集,但是并不是全部。另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。通俗来说,你可以用HTTP协议传输非Html数据,就是这样=。=再简单来说,层级不一样。 - -# Websocket是什么样的协议,具体有什么优点 - -首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。 - -HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么在HTTP1.0中,这次HTTP请求就结束了。 - -在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。 - -但是请记住 Request = Response,在HTTP中永远是这样,也就是说一个request只能有一个response。 - -而且这个response也是被动的,不能主动发起。 - -**教练,你BB了这么多,跟Websocket有什么关系呢?** - -_(:з」∠)_好吧,我正准备说Websocket呢。。 - -首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。在握手阶段是一样的 - --------以下涉及专业技术内容,不想看的可以跳过,或者只看加黑内容-------- - -首先我们来看个典型的Websocket握手(借用Wikipedia的。。) - -```bash -GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 -Origin: http://example.com -``` - -熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。我会顺便讲解下作用。 - -```bash -Upgrade: websocket -Connection: Upgrade -``` - -这个就是Websocket的核心了,告诉Apache、Nginx等服务器:**注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。** - -```bash -Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 -``` - -首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:**泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。** - -然后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:**今晚我要服务A,别搞错啦~** - -最后,Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。。不过现在还好,已经定下来啦~大家都使用的一个东西~ 脱水:**服务员,我要的是13岁的噢→_→** - -然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦! - -```bash -HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= -Sec-WebSocket-Protocol: chat -``` - -这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~ - -```bash -Upgrade: websocket -Connection: Upgrade -``` - -依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。 - -然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:**好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。** - -后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。 - -至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。具体的协议就不在这阐述了。 - -------------------技术解析部分完毕------------------ - -![](https://pic2.zhimg.com/50/afe119b52e096016139edabc2dfa9661_hd.jpg?source=1940ef5c) - -你TMD又BBB了这么久,那到底Websocket有什么鬼用,http long poll,或者ajax轮询不都可以实现实时信息传递么。 - -![](https://pic4.zhimg.com/50/20110e661edb1e93755a99c1d826e264_hd.jpg?source=1940ef5c) - -好好好,年轻人,那我们来讲一讲Websocket有什么用。来给你吃点胡(苏)萝(丹)卜(红) - -![](https://pic2.zhimg.com/50/31ddf0cfbeecef21568d85ca60b5f1ff_hd.jpg?source=1940ef5c) - - -# Websocket的作用 - -在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。 - -首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。 - -场景再现: - -```bash -客户端:啦啦啦,有没有新信息(Request) -服务端:没有(Response) -客户端:啦啦啦,有没有新信息(Request) -服务端:没有。。(Response) -客户端:啦啦啦,有没有新信息(Request) -服务端:你好烦啊,没有啊。。(Response) -客户端:啦啦啦,有没有新消息(Request) -服务端:好啦好啦,有啦给你。(Response) -客户端:啦啦啦,有没有新消息(Request) -服务端:。。。。。没。。。。没。。。没有(Response) ---- loop -``` - -long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。 - -场景再现: - -```bash -客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -服务端:额。。 等待到有消息的时候。。来 给你(Response) -客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) ---- loop -``` - -从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。 - -何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。 - -简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。 - -说完这个,我们再来说一说上面的缺陷(原谅我废话这么多吧OAQ) - -从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。 - -ajax轮询 需要服务器有很快的处理速度和资源。(速度) - -long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小) - -所以ajax轮询 和long poll 都有可能发生这种情况。 - -```bash -客户端:啦啦啦啦,有新信息么? -服务端:月线正忙,请稍后再试(503 Server Unavailable) -客户端:。。。好吧,啦啦啦,有新信息么? -服务端:月线正忙,请稍后再试(503 Server Unavailable) -客户端:。。。 -``` - -然后服务端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我错了。。这又是梗。。) - --------------------------- - -言归正传,我们来说Websocket吧通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。 - -一种需要更快的速度,一种需要更多的'电话'。这两种都会导致'电话'的需求越来越高。 - -哦对了,忘记说了HTTP还是一个无状态协议。(感谢评论区的各位指出OAQ) - -通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。 - -所以在这种情况下出现了,Websocket出现了。 - -他解决了HTTP的这几个难题。 - -首先,**被动性**,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。 - -所以上面的情景可以做如下修改: -```bash -客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request) -服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched) -客户端:麻烦你有信息的时候推送给我噢。。 -服务端:ok,有的时候会告诉你的。 -服务端:balabalabalabala -服务端:balabalabalabala -服务端:哈哈哈哈哈啊哈哈哈哈 -服务端:笑死我了哈哈哈哈哈哈哈 -``` - -就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你) - -这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。 - -那么为什么他会解决服务器上消耗资源的问题呢? - -其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。 - -简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)。 - -本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢。,导致客服不够。 - -Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员在统一转交给客户。这样就可以解决客服处理速度过慢的问题了。 - -同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输identity info(鉴别信息),来告诉服务端你是谁。 - -虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。 - -但是Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。 - -同时由客户主动询问,转换为服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。),没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的客服(Handler)了 - --------------------- - -至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:不能 - -但是可以通过上面说的 long poll 和 ajax 轮询来 模拟出类似的效果 - - - -# 祝君无Bug~ - - -> 作者:Ovear -> -> 链接:[点击查看原文](https://www.zhihu.com/question/20215561/answer/40316953) -> -> 来源:知乎 \ No newline at end of file diff --git "a/source/_posts/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243.md" "b/source/_posts/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243.md" deleted file mode 100644 index ef5114d84..000000000 --- "a/source/_posts/\347\250\213\345\272\217\345\221\230\350\277\233\344\277\256\346\226\207\346\241\243.md" +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: 程序员进修文档 -categories: - - 个人文档 -tags: - - 个人文档 -keywords: 个人文档 -description: 个人文档 -cover: https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201111170309.png -date: 2020-06-15 08:16:29 ---- - -> 以下文档为学习期间整理的好文,有的只是大体的看了一下觉得不错就收藏了,一定要找机会读完。以后要强烈拒绝这种码了不看的行为!!! - -# 程序员修炼手册 -- [给程序员的忠告](https://www.cnblogs.com/jpwz/p/6381832) -- [不要做浮躁的程序员](https://www.cnblogs.com/thisway/p/5055132.html) -- [力扣(LeetCode)题库](https://leetcode-cn.com/problemset/all/) -- [LeetCode解题之路](https://github.com/azl397985856/leetcode?utm_source=gold_browser_extension) -- [Web 开发技术 - MDN web docs](https://developer.mozilla.org/zh-CN/docs/Web) -- [W3C 和 MDN 简介](https://juejin.im/post/5c0e86c3e51d4529a9031832) -- [停止学习框架,应多学习不会过时的通用技能](https://juejin.im/post/5c1a839f518825780008537d) -- [计算机专业曾经有哪些方向非常火后来却凉了?](https://www.zhihu.com/question/323592434/answer/685829847) - - -# 前端面试题 - -## [前端100问:能搞懂80%的请把简历给我](https://juejin.im/post/5d23e750f265da1b855c7bbe?utm_source=gold_browser_extension) -#### [JavaScript专题系列二十篇正式完结](https://github.com/mqyqingfeng/Blog/issues/53) -## [web前端面试100题带答案(知乎)](https://zhuanlan.zhihu.com/p/82124513) - -- [vue常见面试题-知乎](https://zhuanlan.zhihu.com/p/92407628) -- [前端常见的Vue面试题目汇总](https://zhuanlan.zhihu.com/p/127186829) -- [前端每日3+1面试题](https://github.com/haizlin/fe-interview?utm_source=gold_browser_extension) -- [前端小智 博客目录](https://github.com/qq449245884/xiaozhi) -- [程序媛面试之高频题型汇总(一)](https://juejin.im/post/5ca33cdfe51d451fc12208d7) -- [程序媛面试之高频题型汇总(二)](https://juejin.im/post/5ca4a835518825381f7da889) -- [Web前端知识总结(逼乎)](https://zhuanlan.zhihu.com/p/25334672) -- [为什么Node是小菜前端团队的核心技术栈](https://juejin.im/post/5ca321f76fb9a05e5d09bb8a) -- [2019前端面试题汇总(主要是Vue)](https://zhuanlan.zhihu.com/p/57338228) -- [记一次腾讯社招前端面试(已拿到offer入职)](https://juejin.im/post/5dde65496fb9a07161483fc9?utm_source=gold_browser_extension) -- [算法面经分享|双非研究生斩获大厂offer](https://blog.csdn.net/Kaiyuan_sjtu/article/details/105292655) - - - - -# 学习资料 -- [学习资源 - ApacheCN](https://home.apachecn.org/docs/) - - - - -# JavaScript -- [ECMAScript 6 入门 - 阮一峰](http://es6.ruanyifeng.com/) -- [ES6、ES7、ES8、ES9、ES10新特性一览](https://juejin.im/post/5ca2e1935188254416288eb2#heading-20) -- [JS开发必须知道的41个技巧【持续更新】](https://juejin.im/post/6854573212890562573) -- [重新介绍 JavaScript(JS教程)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript) -- [(建议收藏)原生JS灵魂之问,请问你能接得住几个?(上)](https://juejin.im/post/5dac5d82e51d45249850cd20) -- [(建议精读)原生JS灵魂之问(中),检验自己是否真的熟悉JavaScript?](https://juejin.im/post/5dbebbfa51882524c507fddb) -- [(2.4w字,建议收藏)😇原生JS灵魂之问(下),冲刺🚀进阶最后一公里(附个人成长经验分享)](https://juejin.im/post/5dd8b3a851882572f56b578f) -- [你不懂的JS(系列丛书)](https://github.com/JoeHetfield/You-Dont-Know-JS) -- [JacaScript的11个技巧(new Set)](https://juejin.im/post/5ce88a3951882533182d7e7d) -- [一个合格的中级前端工程师必须要掌握的 28 个 JavaScript 技巧](https://juejin.im/post/5cef46226fb9a07eaf2b7516?utm_source=gold_browser_extension) -- [译,送你43道JavaScript面试题](https://juejin.im/post/5d0644976fb9a07ed064b0ca?utm_source=gold_browser_extension) -- [灵活使用console让js调试更简单](https://juejin.im/post/5ca6bf5151882543fc5e3bb0) -- [前端的10根救命稻草🍃](https://juejin.im/post/5d904712e51d45781e0f5dd0?utm_source=gold_browser_extension) -- [34 个今年11月最受欢迎的JavaScript库](https://juejin.im/post/5df65fbdf265da33d83e70b7?utm_source=gold_browser_extension) -- [隐藏实力的 JSON.stringify,原来还可以这么用!](https://juejin.im/post/5ddc8da56fb9a07ad739f00b?utm_source=gold_browser_extension) - - - -# Vue -- [awesome-vue, GitHub](https://github.com/vuejs/awesome-vue#components--libraries) -- [Vue 开发必须知道的36个技巧【近1W字】](https://juejin.im/post/5d9d386fe51d45784d3f8637?utm_source=gold_browser_extension) -- [剖析Vue原理之发布订阅者模式(基础篇)](https://juejin.im/post/5ca456bfe51d456d3c45fef4) -- [react和vue有什么区别吗?](https://juejin.im/post/5ca6fd2f6fb9a05e643def4f) -- [vue全家桶开发的一些小技巧和注意事项(vue + vuex + vue-router + axios + less + elementUI)](https://juejin.im/post/5d8c6a97e51d45782c23fa69) - - - -# 算法 -- [写给前端的算法进阶指南,我是如何两个月零基础刷200题](https://juejin.im/post/6847009772500156429) -- [让你瞬间提高工作效率的常用js函数汇总(持续更新)](https://juejin.im/post/5d1a45b0f265da1bb277494c) -- [数组去重 6法](https://juejin.im/post/5cffac5de51d45777b1a3d6f) -- [如何让前端代码速度提高60倍](https://juejin.im/post/5d034e83e51d45773e418a69) -- [forEach、map、filter、find、sort、find等易错点整理](https://juejin.im/post/5ca96c76f265da24d5070563) -- [WEB前端必须掌握的一些算法题: 回文、去重、统计、排序、交换、canvas斐波那契数列、最大差、随机生成字符串、实现类似getElementsByClassName、JS 实现二叉查找树](https://blog.csdn.net/weixin_38984353/article/details/80393412) - - - -# 浏览器 -- [(1.6w字)浏览器与前端性能灵魂之问,请问你能接得住几个?(上)](https://juejin.im/post/5df5bcea6fb9a016091def69?utm_source=gold_browser_extension) - - - -# 代码规范 -- [前端团队代码评审 CheckList 清单](https://juejin.im/post/5d1c6550518825330a3bfa01?utm_source=gold_browser_extension) -- [前端工具类项目规范化-使用TS](https://juejin.im/post/5ca6dd5c6fb9a05e2b24017a) - - - -# Java -- [从Java基础、JavaWeb基础到常用的框架再到面试题](https://github.com/ZhongFuCheng3y/3y) - - - -# 工具篇 -- [欲善事先利器——系统篇](https://juejin.im/post/5e71a4b2e51d4526c70fca2d?utm_source=gold_browser_extension) -- [程序员必备,效率提升10倍,Mac上那些颜值高功能强大的软件,我只推荐这10个!](https://juejin.im/post/5e7a0fbbe51d4526fe65386e?utm_source=gold_browser_extension) - - - - - - - - -# 祝君无Bug~ - - - - diff --git "a/source/_posts/\351\242\204\351\230\262XSS\346\224\273\345\207\273.md" "b/source/_posts/\351\242\204\351\230\262XSS\346\224\273\345\207\273.md" deleted file mode 100644 index 63ff7758d..000000000 --- "a/source/_posts/\351\242\204\351\230\262XSS\346\224\273\345\207\273.md" +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: 预防XSS攻击插件 js-xss -categories: - - Vue -tags: - - Vue -keywords: Vue -description: 预防XSS攻击 -cover: 'https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20210421184346.png' -date: 2021-04-02 21:49:05 ---- - -> 在面向客户开发时,特别是根据客户输入的内容进行入库试,无法预计会输入什么,所以需要对客户输入的内容进行过滤,以免引起不必要的bug甚至数据库崩掉 - -# 参考网站 - -- [github:js-xss](https://github.com/leizongmin/js-xss/blob/master/README.zh.md) -- [项目主页](http://jsxss.com) -- [在线测试](http://jsxss.com/zh/try.html) - - -# 安装插件 - -```bash -npm install xss -``` - -# 引入插件 - -```js -import { getDefaultWhiteList, FilterXSS } from 'xss'; -``` - -# 封装函数 - -插件的github文档中给出了很多api,根据自己的需求进行封装即可,如下所示: - -```js -export const filterXSS = (() => { - const whiteList: any = getDefaultWhiteList(); // 获取默认白名单 - // 添加新的白名单标签或属性 - for (const i of Object.keys(whiteList)) { - whiteList[i].push('style', 'class'); - if (i === 'table' && whiteList[i]) { - whiteList[i].push('cellpadding', 'cellspacing', 'bordercolor'); - } - } - whiteList.strike = ['style', 'class']; - const options = { - whiteList, - css: false, - stripIgnoreTag: true, // 非白名单标签,过滤标签,显示标签里的内容 - stripIgnoreTagBody: ['script', 'style'], - onTagAttr(tag: any, name: any, value: any, isWhiteAttr: any) { - // 过滤img标签的src属性 - if (tag === 'img' && name === 'src') { - return `${name}="data:image/ico;base64,aWNv" data-${name}=${value}`; - } - // 处理a标签 - if (tag === 'a') { - return `${name}=${value} style="pointer-events: none;"`; - } - // 如果不返回任何值,表示还是按照默认的方法处理 - }, - }; - const myxss = new FilterXSS(options); - return myxss.process.bind(myxss); -})(); -``` - -# 使用方法 - -```js -import { filterXSS } from '@/utils'; - -var filterData = filterXSS(res.data); -console.log(filterData); -``` - - -# 祝君无Bug~ \ No newline at end of file diff --git a/source/categories/index.md b/source/categories/index.md deleted file mode 100644 index 401b411f4..000000000 --- a/source/categories/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 分类 -date: 2020-06-08 09:11:36 -type: "categories" ---- diff --git a/source/resume/index.md b/source/resume/index.md deleted file mode 100644 index d08b122b9..000000000 --- a/source/resume/index.md +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: 个人简历 -date: 2021-04-23 18:32:14 ---- - -# 壹。 自我介绍 - -## 个人信息 -- 头像: -- ![个人头像](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0608fee8fda46abb87427a452c591ef~tplv-k3u1fbpfcp-zoom-1.image) -- 姓名:李淳淳 -- 求职意向:**前端开发**想转**全栈开发**(对大数据、人工智能感兴趣) -- 联系方式:15610045821 -- 电子邮箱:961150665@qq.com -- 个人博客:[淳淳同学的个人博客](https://leedebug.github.io/) - -## 技能掌握 -- 熟练运用:vue2.x、vue3.x、Element Plus、Vuex、Vue-Router、... -- 灵活使用:Webpack、qiankun、TypeScript、Vite、Bus、微信小程序... -- 额外了解:Electron、Django + Restful Api、Taro、Uni-App、... - -## 教育经历 -- 专业硕士研究生(非全日制),青岛大学(2018.9-2021.7),计算机技术; -- 本科,青岛理工大学(2014.9-2018.7),网络工程; - -## 个人评价 -- 坚持`6`年相声演出;坚持`3`年每天写日记; -- 喜欢研究阅读源码;喜欢对新技术落地实施; -- 细节把握成败;勇于犯错且敢于承担责任; -- 性格外向开朗;善于团队合作; - - -# 贰。 项目详细介绍 - -## V7联络云 - 访客端(PC + H5) - -从2020-12-01至今历时7个月,已迭代至V3.4.2版本。 - -```mermaid -gantt - -title 进度周期(甘特图) -dateFormat YYYY-MM-DD - -section 业务逻辑 -需求评审 :a1, 2020-12-01, 20d -V1.0 :a2, 2020-12-22, 66d -V2.0 :a3, 2021-03-01, 45d -V3.0 :a4, 2021-04-18, 70d -``` - -### 介绍 - -打造一款`更轻便的消息渲染器`,让客户仅需要添加一段`script代码`即可为自己的网站提供`智能在线客服的能力`(手机官网、app、小程序等跳转H5链接即可)。 - -```js -// 技术栈(无任何UI框架) -"axios": "0.21.0", -"qiniu-js": "3.1.2", -"socket.io-client": "2.3.0", -"v3-carousel": "1.1.1", // 自己封装并发布npm的开源轮播组件 -"vue": "3.0.4", -"vue-i18n": "9.0.0-beta.15", -"vuex": "4.0.0-rc.2", -"xss": "1.0.8", -"crypto-js": "4.0.0", -"stylus": "0.54.7", -"typescript": "4.1.3", -"vite": "2.3.4", -``` - -### 功能 - -- `自动邀请框`:首次自动弹出、关闭后n秒重新弹出、配置可弹出时机、快捷咨询发起会话、自定义样式、缩小后未读消息提示等 -- `机器人流程`:联想输入、快捷回复按钮、有无帮助、关键词及按钮转人工等 -- `人工咨询流程`:文本消息、富文本消息、emoji表情、文件图片视频、满意度评价(主动、被动、结束会话)、粘贴图片即发送、发送loading与失败重发、已读未读、发送消息节流、会话转接等 -- `留言流程`:表单留言、文本消息留言等 -- 自定义主题色、自定义弹窗及咨询按钮位置、会话流程日志打印等 -- 多处轮播广告栏、首次广告语推送、自定义图片视频、iframe嵌入等 -- 刷新页面记忆流程节点、socket心跳检测及意外断开重连、消息时序问题、多端登录互踢等 - -### 亮点 - -- 多入口打包,同时兼容`PC`和`H5`的方式打开 -- 更完善的流程与状态管理,可拖拽的配置方式,客户可自由搭配 -- 智能引导菜单、机器人、人工、留言、广告推送等多种留资方式 -- 多种自定义样式模板:`pc`模板、`h5`模板、广告模板、小程序模板 -- 配置信息、聊天内容、`socket`推送等敏感信息用`crypto-js`加密 - -### 难点 - -- 首次从〇开始初始化 `vue3` + `ts` 项目 -- 项目多入口打包方式的构思与实现 -- `socket` 的事件推送与处理维护机制 -- `emoji` 表情采用雪碧图(精灵图)的形式展示 -- 消息列表渲染、消息类型区分、列表滚动机制 -- 移动端兼容问题(键盘遮挡、hover样式不消失等) -- 客户页与`iframe`内部通过`postMessage`事件通信 -- 前端用`sendStatus`字段维护消息的状态 -- 使用`xss`的`FilterXSS`方法进行消息过滤 -- 把富文本消息要绑定的函数挂载到`window`上即可调用 -- IOS上点击输入框会被弹起的键盘遮挡住 -- IOS上不支持直接使用`transparent`透明属性 -- Android微信浏览器,点击`type=file`的`input`框选取文件会报错 -- 手机端上元素点击后`:hover`样式不会自动消失 -- 图片加载小图、预加载骨架 - ---- - -## V7联络云 - 访客端(微信小程序插件) - -```mermaid -gantt - -title 进度周期(甘特图) -dateFormat YYYY-MM-DD - -section 业务逻辑 -需求评审 :a1, 2021-05-20, 12d -V1.0 :a2, 2021-06-01, 30d -``` - -### 介绍 - -将访客端以插件的形式移至微信小程序内,宿主(即插件调用者)仅需使用插件版本号与插件开发者的AppId即可实现智能客服能力。 - -```js -// 技术栈: -"@vant/weapp": "1.0.0", -"crypto-js": "4.0.0", -"miniprogram-api-promise": "1.0.4", -"miniprogram-computed": "4.0.4", -"mobx-miniprogram": "4.13.2", -"mobx-miniprogram-bindings": "1.2.1", -"mp-html": "2.1.3", -"weapp.socket.io": "2.1.0" -``` - -### 功能 - -- 大部分功能同h5访客端 -- 座席端的样式配置、基座小程序的功能配置 -- Emm... - -### 亮点 - -- Emm... - -### 难点 - -- uni-app ==> mp-vue ==> wepy ==> taro/cli ==> 原生 + npm -- 宿主与插件的事件交互机制问题(宿主直接调用插件函数,传参数或回调函数) -- 更新mobx中的引用类型数据后,无法触发视图更新(list=list,obj=Object.assign(obj, obj1)) -- 在js文件中的非Component模块中以及mobx中,无法获取dom节点(使用scroll-view的scroll-top属性代替手动滚动行为,定义一个超大数值COUNT++) -- 不能出现文件循环引用的问题(缺点即:无法将mobx模块化) -- socket没有自带的心跳检测(需要配合计时器手动监听) -- 没有style标签的概念,无法创建全局样式(只能通过mobx进行数据绑定) -- 微信开发者工具的BUG:wxml没有a标签、wxss不能穿透、在插件内调用宿主的路由跳转api报错、text标签内不能换行、插件内不能使用webview_App({})_getApp()方法 - ---- - -## V7联络云 - -```mermaid -gantt - -title 进度周期(甘特图) -dateFormat YYYY-MM-DD - -section 业务逻辑 -需求评审 :a1, 2020-11-01, 50d -V1.0 :a2, 2020-12-22, 66d -V2.0 :a3, 2021-03-01, 45d -V3.0 :a4, 2021-04-18, 70d -``` - -### 介绍 - -采用蚂蚁金融科技提供的 `qiankun` 微前端框架,将我司现有的 `零代码平台` 产品 [Mopower](https://power.7moor.com/home/workbench) 嵌入到新开发的 `V7联络云` 中,实现客户与工单模块的表单配置、用户角色的权限配置、以及客户的关联与应用等功能。 - -### 功能 - -- 通话模块和在线客服模块,以及相对应的数据报表中心 -- 客户模块和工单模块,以及相对应的表单配置模块 -- 消息中心模块,全局的接入、转接、下载、导入导出的提示 -- 知识库模块,为在线客服提供更多智能话术模板 -- OEM自定义域名配置及相关私密信息脱敏 - -### 亮点 - -- qiankun框架的应用解决了iframe的跳转与数据交互的痛点 -- 使用`qiankun`微前端框架嵌入其他子应用,并将自身作为子应用嵌入其他基座 -- 封装vue-router,根据接口数据动态生成路由,且可使用多个字段进行权限控制 - -### 难点 - -- 基座与子应用之间的样式污染问题,特别是子应用插入到基座Body中的Dom元素(采用BEM命名规范的思想与postcss命名空间插件共同解决)。在不开沙箱的情况下使用`postcss`的插件解决`css`样式污染和`iconfont`冲突问题 -- 使用 bus + keep-alive + vue生命周期 封装qiankun的子应用组件 - ---- - -## 七陌官网重构 - -```mermaid -gantt - -title 进度周期(甘特图) -dateFormat YYYY-MM-DD - -section 业务逻辑 -需求评审 :a1, 2020-11-09, 38d -V2.0 :a2, 2020-12-18, 53d -V2.1 :a3, 2021-02-20, 25d -V2.2 :a4, 2021-04-10, 33d -``` - -### 介绍 - -由于容联集团于 2021年02月09日 赴美上市,所以对容联七陌的 [PC端官网](https://www.7moor.com/) 和 [移动端官网](https://m.7moor.com/) 进行代码重构及新页面的改版。 - -### 功能 - -- 使用 vue-router 进行路由改版 -- 引入 gio 埋点 - -### 亮点 - -- 使用 [PrerenderSPAPlugin](https://github.com/chrisvfritz/prerender-spa-plugin) 对单页面官网应用进行预渲染 - -### 难点 - -- @import引入的css不走px3rem-loaders,改为src的引入形式 -``` -- -``` - - -# 叁。 模拟问答 - -### 负责过什么项目,以及在每个项目中的职责? -答: -- 七陌专注于做企业通信领域的SAAS云服务提供商,是一家提供智能客服解决方案的。 -- 虽然我入职仅一年,但已经独立完整的负责过两个项目: - - 一个是智能客服的访客端,分别打包成js接入和h5链接的形式; - - 另一个项目也是访客端,是把现有的访客端开发成原生小程序插件的形式(也就是SDK的形式); -- 还有客服座席端的客户、工单等模块,采用qiankun微前端框架嵌入了子项目的重要模块; -- 还有pc端和wap端七陌官网的重构; - -### 遇到过什么困难的问题,以及解决方案? -答: -- webpack多入口打包 -- 访客端ui高度自定义化 -- qiankun样式污染问题 -- 消息列表渲染,滚动时机 -- 利用雪碧图做emoji表情墙 -- 小程序宿主与插件的事件交互 -- vue3 + ts 学习成本 - -### 平时如何学习?如何做到 WLB ? -答: -- 白天认真工作,工作才是第一生产力 -- 晚上总结输出,只有多产出才能记住 -- 平时多交流沟通,每个月会做1-2次技术分享 -- 定时更新博客,记录学到的内容并学会总结分享 - - -# 肆。 备注 - -写点儿啥呢。。。 - diff --git a/source/tags/index.md b/source/tags/index.md deleted file mode 100644 index 744242e8c..000000000 --- a/source/tags/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 标签 -date: 2020-06-08 09:12:11 -type: "tags" ---- diff --git a/tags/CSS/index.html b/tags/CSS/index.html new file mode 100644 index 000000000..b132302ae --- /dev/null +++ b/tags/CSS/index.html @@ -0,0 +1,237 @@ +Tag: CSS | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/Git/index.html b/tags/Git/index.html new file mode 100644 index 000000000..b628f2152 --- /dev/null +++ b/tags/Git/index.html @@ -0,0 +1,237 @@ +Tag: Git | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/JavaScript/index.html b/tags/JavaScript/index.html new file mode 100644 index 000000000..051b8fc11 --- /dev/null +++ b/tags/JavaScript/index.html @@ -0,0 +1,237 @@ +Tag: JavaScript | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/LeetCode/index.html b/tags/LeetCode/index.html new file mode 100644 index 000000000..72447ef3c --- /dev/null +++ b/tags/LeetCode/index.html @@ -0,0 +1,237 @@ +Tag: LeetCode | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/Socket/index.html b/tags/Socket/index.html new file mode 100644 index 000000000..8530ca001 --- /dev/null +++ b/tags/Socket/index.html @@ -0,0 +1,237 @@ +Tag: Socket | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/VSCode/index.html b/tags/VSCode/index.html new file mode 100644 index 000000000..a71d45cd5 --- /dev/null +++ b/tags/VSCode/index.html @@ -0,0 +1,237 @@ +Tag: VSCode | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/Vue/index.html b/tags/Vue/index.html new file mode 100644 index 000000000..4c91f59ef --- /dev/null +++ b/tags/Vue/index.html @@ -0,0 +1,237 @@ +Tag: Vue | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/Webpack/index.html b/tags/Webpack/index.html new file mode 100644 index 000000000..fb983d497 --- /dev/null +++ b/tags/Webpack/index.html @@ -0,0 +1,237 @@ +Tag: Webpack | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 000000000..9f2e2dedf --- /dev/null +++ b/tags/index.html @@ -0,0 +1,239 @@ +标签 | 淳淳同学的个人博客 + + + + + + + + + + + +
    \ No newline at end of file diff --git a/tags/npm/index.html b/tags/npm/index.html new file mode 100644 index 000000000..08005d71a --- /dev/null +++ b/tags/npm/index.html @@ -0,0 +1,237 @@ +Tag: npm | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" "b/tags/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" new file mode 100644 index 000000000..88e9ff954 --- /dev/null +++ "b/tags/\344\270\252\344\272\272\346\226\207\346\241\243/index.html" @@ -0,0 +1,237 @@ +Tag: 个人文档 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\345\276\256\345\211\215\347\253\257/index.html" "b/tags/\345\276\256\345\211\215\347\253\257/index.html" new file mode 100644 index 000000000..7c601c428 --- /dev/null +++ "b/tags/\345\276\256\345\211\215\347\253\257/index.html" @@ -0,0 +1,237 @@ +Tag: 微前端 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\346\234\215\345\212\241\345\231\250/index.html" "b/tags/\346\234\215\345\212\241\345\231\250/index.html" new file mode 100644 index 000000000..1e6ad985a --- /dev/null +++ "b/tags/\346\234\215\345\212\241\345\231\250/index.html" @@ -0,0 +1,237 @@ +Tag: 服务器 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\347\247\273\345\212\250\347\253\257/index.html" "b/tags/\347\247\273\345\212\250\347\253\257/index.html" new file mode 100644 index 000000000..a5a416342 --- /dev/null +++ "b/tags/\347\247\273\345\212\250\347\253\257/index.html" @@ -0,0 +1,237 @@ +Tag: 移动端 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" "b/tags/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" new file mode 100644 index 000000000..7b812d796 --- /dev/null +++ "b/tags/\350\257\273\344\271\246\347\254\224\350\256\260/index.html" @@ -0,0 +1,237 @@ +Tag: 读书笔记 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\351\235\242\350\257\225-Vue-JavaScript/index.html" "b/tags/\351\235\242\350\257\225-Vue-JavaScript/index.html" new file mode 100644 index 000000000..53ed9d4b6 --- /dev/null +++ "b/tags/\351\235\242\350\257\225-Vue-JavaScript/index.html" @@ -0,0 +1,237 @@ +Tag: 面试,Vue,JavaScript | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git "a/tags/\351\235\242\350\257\225/index.html" "b/tags/\351\235\242\350\257\225/index.html" new file mode 100644 index 000000000..4513292cd --- /dev/null +++ "b/tags/\351\235\242\350\257\225/index.html" @@ -0,0 +1,237 @@ +Tag: 面试 | 淳淳同学的个人博客 + + + + + + + + + +
    \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/bug_report.md b/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100755 index f8c2d6211..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - - - - - - -## I want to create a Bug report - - - -- [] Yes, I have read [Hexo Docs page](https://hexo.io/docs/), especially [Templates](https://hexo.io/docs/templates.html), [Variables](https://hexo.io/docs/variables.html), [Helpers](https://hexo.io/docs/helpers.html) and [Troubleshooting](https://hexo.io/docs/troubleshooting.html). -- [] Yes, I have read [Butterfly Documentation](https://demo.jerryc.me/posts/21cfbf15/). -- [] And yes, I already searched for current [issues](https://github.com/jerryc127/hexo-theme-butterfly/issues?utf8=%E2%9C%93&q=is%3Aissue) and this did not help me. - -## Butterfly Information - - - -**Butterfly Version:** - - -**Platform:** - - -**Browser:** - -## Expected behavior - -## Actual behavior - -## Steps to reproduce the behavior -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - - - -## Screenshots - - -## Website - -## Describe the bug diff --git a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/custom.md b/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100755 index 4842758e5..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: Custom issue template -about: Describe this issue template's purpose here. -title: '' -labels: '' -assignees: '' ---- - - diff --git a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/feature_request.md b/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100755 index bbcbbe7d6..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/themes/hexo-theme-butterfly-3.3.0/.github/stale.yml b/themes/hexo-theme-butterfly-3.3.0/.github/stale.yml deleted file mode 100755 index 2d74a9cb8..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/.github/stale.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security - - bug - - enhancement - - documentation -# Label to use when marking an issue as stale -staleLabel: wontfix -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/LICENSE b/themes/hexo-theme-butterfly-3.3.0/LICENSE deleted file mode 100755 index 7a4a3ea24..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/README.md b/themes/hexo-theme-butterfly-3.3.0/README.md deleted file mode 100755 index 0aa28f60a..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# hexo-theme-butterfly - -![version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly) -![https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff) -![hexo version](https://img.shields.io/badge/hexo-4.2+-0e83c) -![npm download](https://img.shields.io/npm/dw/hexo-theme-butterfly?color=green) -![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531) - -Demo: 👍 [Butterfly](https://demo.jerryc.me/) || 🤞 [JerryC](https://jerryc.me/) - -Docs: 📖 [Butterfly Docs](https://demo.jerryc.me/posts/21cfbf15/) - -Based on [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) theme. - -## Installation - -### GIT - -> If you are in Mainland China, you can download in [Gitee](https://gitee.com/iamjerryw/hexo-theme-butterfly) - -Stable branch [recommend]: - -``` -git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly -``` - -Dev branch: - -``` -git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly -``` - -### NPM - -> It supports Hexo 5.0.0 or later - -In Hexo site root directory - -```powershell -npm i hexo-theme-butterfly -``` - -## Configuration - - Set theme in the hexo work folder's root config file `_config.yml`: - -> theme: butterfly - - If you don't have pug & stylus renderer, try this: - -> npm install hexo-renderer-pug hexo-renderer-stylus - -## Features - -- [x] Card UI Design -- [X] Support sub-menu -- [x] Two Column designs -- [x] Responsive Web Design -- [x] Dark Mode -- [x] Pjax -- [x] Read Mode -- [x] Conversion between Traditional and Simplified Chinese -- [X] TOC catalog is available for both computers and mobile phones -- [X] Color themes (darker/pale night/light/ocean/mac/mac light), support custom colors -- [X] Code Blocks (Display code language/close or expand Code Blocks/Copy Button/word wrap) -- [X] Disable copy/Add a Copyright Notice to the Copied Text -- [X] Search (Algolia SearchZ/Local Search) -- [x] Mathjax and Katex -- [x] Built-in 404 page -- [x] WordCount -- [x] Related articles -- [x] Displays outdated notice for a post -- [x] Share (AddThis/Sharejs/Addtoany) -- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Utterances/Facebook Comments) -- [x] Multiple Comment System Support -- [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp) -- [x] Web analytics (Baidu Analytics/Google Analytics/Tencent Analytics/CNZZ Analytics) -- [x] Google AdSense -- [x] Webmaster Verification (google/Bing/Baidu/360/Yandex) -- [x] Change website colour scheme -- [x] Typewriter Effect: activate_power_mode -- [x] Background effects (Canvas ribbon/canvas_ribbon_piao/canvas_nest) -- [x] Mouse click effects (Fireworks/Heart/Text) -- [x] Preloader/Loading Animation -- [x] Busuanzi visitor counter -- [x] Medium Zoom/Fancybox -- [x] Mermaid -- [x] Justified Gallery -- [x] Lazyload images -- [x] Instantpage/Pangu/Snackbar notification toast/PWA...... - -## Screenshots - -![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-1.png) - -![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-2.png) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/README_CN.md b/themes/hexo-theme-butterfly-3.3.0/README_CN.md deleted file mode 100755 index 49ea57fc8..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/README_CN.md +++ /dev/null @@ -1,96 +0,0 @@ -# hexo-theme-butterfly - -![version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly) -![https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff) -![hexo version](https://img.shields.io/badge/hexo-4.2+-0e83c) -![npm download](https://img.shields.io/npm/dw/hexo-theme-butterfly?color=green) -![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531) - - -預覽: 👍 [Butterfly](https://demo.jerryc.me/) || 🤞 [JerryC](https://jerryc.me/) - -文檔: 📖 [Butterfly Docs](https://demo.jerryc.me/posts/21cfbf15/) - -一款基於[hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody)修改的主題 - -## 安裝 - -### Git 安裝 - -> 本倉庫同時上傳到 [Gitee](https://gitee.com/iamjerryw/hexo-theme-butterfly),如果你訪問 Github 緩慢,可從 Gitee 中下載。 - -在博客根目錄裡安裝穩定版【推薦】 - -```powershell -git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly -``` - -如果想要安裝比較新的dev分支,可以 - -```powershell -git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly -``` - -### npm 安裝 - -> 此方法只支持Hexo 5.0.0以上版本 - -在博客根目錄裡 - -```powershell -npm i hexo-theme-butterfly -``` - -## 應用主題 -修改hexo配置文件`_config.yml`,把主題改為`Butterfly` - -``` -theme: butterfly -``` - ->如果你沒有pug以及stylus的渲染器,請下載安裝: npm install hexo-renderer-pug hexo-renderer-stylus --save - -## 特色 - -- [x] 卡片化設計 -- [X] 支持二級目錄 -- [x] 雙欄設計 -- [x] 響應式主題 -- [x] 夜間模式 -- [x] Pjax -- [x] 文章閲讀模式 -- [x] 簡體和繁體轉換 -- [X] 電腦和手機都可查看TOC目錄 -- [X] 內置多種代碼配色(darker/pale night/light/ocean/mac/mac light),可自定義代碼配色 -- [X] 代碼塊顯示代碼語言/關閉或展開代碼塊/代碼複製/代碼自動換行 -- [X] 可關閉文字複製/可開啟內容複製增加版權信息) -- [X] 兩種搜索(Algolia搜索和本地搜索) -- [x] Mathjax 和 Katex -- [x] 內置404頁面 -- [x] 顯示字數統計 -- [x] 顯示相關文章 -- [x] 過期文章提醒 -- [x] 多種分享系統(AddThis/Sharejs/Addtoany) -- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Utterances/Facebook Comments) -- [x] 支持雙評論部署 -- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp) -- [x] 多種分析系統(百度分析/谷歌分析/騰訊分析/CNZZ分析) -- [x] 谷歌廣告/手動廣告位置 -- [x] 各種站長驗證(Google/Bing/Baidu/360/Yandex) -- [x] 修改網站配色 -- [x] 打字特效 activate_power_mode -- [x] 多種背景特效(靜止彩帶/動態彩帶/Canvas Nest) -- [x] 多種鼠標點擊特效(煙花/文字/愛心) -- [x] 內置一種 Preloader 加載動畫 -- [x] 不蒜子訪問統計 -- [x] 兩種大圖模式(Medium Zoom/Fancybox) -- [x] Mermaid 圖表顯示 -- [x] 照片牆 -- [x] 圖片懶加載 -- [x] Instantpage/Pangu/Snackbar彈窗/PWA...... - -## 截圖 - -![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-1.png) - -![](https://cdn.jsdelivr.net/gh/jerryc127/CDN/img/theme-butterfly-readme-homepage-2.png) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/_config.yml b/themes/hexo-theme-butterfly-3.3.0/_config.yml deleted file mode 100755 index 6334ac93e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/_config.yml +++ /dev/null @@ -1,935 +0,0 @@ -# Main menu navigation (導航目錄) -# -------------------------------------- - -# format: name: link || icon -# sub-menu -# name || icon: -# name || link || icon - -menu: - 主页: / || fas fa-home - 标签: /tags/ || fas fa-tags - 分类: /categories/ || fas fa-folder-open - 时间轴: /archives/ || fas fa-archive - # 友情链接: /link/ || fas fa-link - # 关于淳淳同学: /about/ || fas fa-heart - 关于淳淳同学 || fa fa-list: - - 电子简历 || /resume.html || fas fa-file-word - - 掘金社区 || https://juejin.cn/user/2189882894323975 || fas fa-blog - - GitHub || https://github.com/LeeDebug || fab fa-github - - GitLab || https://gitlab.com/Leevue || fab fa-gitlab - - Gitee || https://gitee.com/leejs || fab fa-git - - 微博 || https://weibo.com/p/1005053297895847 || fab fa-weibo - # - 音乐 || /music/ || fa fa-music - # - 电影 || /movies/ || fa fa-film - -# Hide the child menu items in mobile sidebar -hide_sidebar_menu_child: false - -# Code Blocks (代碼相關) -# -------------------------------------- - -highlight_theme: mac light # darker / pale night / light / ocean / mac / mac light / false -highlight_copy: true # copy button -highlight_lang: true # show the code language -highlight_shrink: false # true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button -code_word_wrap: false - -# copy settings -# copyright: Add the copyright information after copied content (複製的內容後面加上版權信息) -copy: - enable: true - copyright: - enable: false - limit_count: 50 - -# social settings (社交圖標設置) -# formal: -# icon: link || the description -social: - fab fa-github: https://github.com/LeeDebug || Github - fas fa-envelope: mailto:961150665@qq.com || Email - -# search (搜索) -# -------------------------------------- - -# Algolia search -algolia_search: - enable: false - hits: - per_page: 6 - -# Local search -local_search: - enable: false - -# Math (數學) -# -------------------------------------- -# About the per_page -# if you set it to true, it will load mathjax/katex script in each page (true 表示每一頁都加載js) -# if you set it to false, it will load mathjax/katex script according to your setting (add the 'mathjax: true' in page's front-matter) -# (false 需要時加載,須在使用的 Markdown Front-matter 加上 mathjax: true) - -# MathJax -mathjax: - enable: false - per_page: false - -# KaTeX -katex: - enable: false - per_page: false - hide_scrollbar: true - -# Image (圖片設置) -# -------------------------------------- - -# Favicon(網站圖標) -favicon: /img/favicon.png - -# Avatar (頭像) -avatar: - img: /img/avatar.png - effect: false # 頭像會一直轉圈 - -# The banner image of home page -index_img: - -# If the banner of page not setting, it will show the top_img -default_top_img: -# https://cdn.jsdelivr.net/gh/LeeDebug/PicGo/img/20201225095521.jpg -# https://i.loli.net/2020/05/01/IuWi8QbHvzjlOPw.jpg - -# The banner image of archive page -archive_img: - -# If the banner of tag page not setting, it will show the top_img -# note: tag page, not tags page (子標籤頁面的 top_img) -tag_img: - -# The banner image of tag page -# format: -# - tag name: xxxxx -tag_per_img: - -# If the banner of category page not setting, it will show the top_img -# note: category page, not categories page (子分類頁面的 top_img) -category_img: - -# The banner image of category page -# format: -# - category name: xxxxx -category_per_img: - -cover: - # display the cover or not (是否顯示文章封面) - index_enable: true - aside_enable: true - archives_enable: true - # the position of cover in home page (封面顯示的位置) - # left/right/both - position: both - # When cover is not set, the default cover is displayed (當沒有設置cover時,默認的封面顯示) - default_cover: - - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg - - https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg.png - - https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg2.png - - https://cdn.jsdelivr.net/gh/jerryc127/CDN@latest/cover/default_bg3.png - -# Replace Broken Images (替換無法顯示的圖片) -error_img: - flink: /img/friend_404.gif - post_page: /img/404.jpg - -# A simple 404 page -error_404: - enable: false - subtitle: 'Page Not Found' - background: https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png - -post_meta: - page: # Home Page - date_type: both # created or updated or both 主頁文章日期是創建日或者更新日或都顯示 - date_format: relative # date/relative 顯示日期還是相對日期 - categories: true # true or false 主頁是否顯示分類 - tags: false # true or false 主頁是否顯示標籤 - label: true # true or false 顯示描述性文字 - post: - date_type: both # created or updated or both 文章頁日期是創建日或者更新日或都顯示 - date_format: date # date/relative 顯示日期還是相對日期 - categories: true # true or false 文章頁是否顯示分類 - tags: true # true or false 文章頁是否顯示標籤 - label: true # true or false 顯示描述性文字 - -# wordcount (字數統計) -wordcount: - enable: true - post_wordcount: true - min2read: true - total_wordcount: true - -# Display the article introduction on homepage -# 1: description -# 2: both (if the description exists, it will show description, or show the auto_excerpt) -# 3: auto_excerpt (default) -# false: do not show the article introduction -index_post_content: - method: 3 - length: 500 # if you set method to 2 or 3, the length need to config - -# Post -# -------------------------------------- - -# toc (目錄) -toc: - enable: true - number: true - -post_copyright: - enable: true - decode: false - license: CC BY-NC-SA 4.0 - license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/ - -# Sponsor/reward -reward: - enable: true - QR_code: - - img: /img/wechat.png - link: - text: 微信 - - img: /img/alipay.png - link: - text: 支付宝 - -# Related Articles -related_post: - enable: true - limit: 6 # Number of posts displayed - date_type: created # or created or updated 文章日期顯示創建日或者更新日 - -# figcaption (圖片描述文字) -photofigcaption: false - -# anchor -# when you scroll in post, the URL will update according to header id. -anchor: false - -# Displays outdated notice for a post (文章過期提醒) -noticeOutdate: - enable: false - style: flat # style: simple/flat - limit_day: 500 # When will it be shown - position: top # position: top/bottom - message_prev: It has been - message_next: days since the last update, the content of the article may be outdated. - -# Share System (分享功能) -# -------------------------------------- - -# AddThis -# https://www.addthis.com/ -addThis: - enable: false - pubid: - -# Share.js -# https://github.com/overtrue/share.js -sharejs: - enable: true - sites: facebook,twitter,wechat,weibo,qq - -# AddToAny -# https://www.addtoany.com/ -addtoany: - enable: false - item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link - -# Comments System -# -------------------------------------- - -comments: - # Up to two comments system, the first will be shown as default - # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Utterances/Facebook Comments - use: - - Valine - # - Disqus - text: true # Display the comment name next to the button - # lazyload: The comment system will be load when comment element enters the browser's viewport. - # If you set it to true, the comment count will be invalid - lazyload: true - count: true # Display comment count in top_img - -# disqus -# https://disqus.com/ -disqus: - shortname: - -# Alternative Disqus - Render comments with Disqus API -# DisqusJS 評論系統,可以實現在網路審查地區載入 Disqus 評論列表,兼容原版 -# https://github.com/SukkaW/DisqusJS -disqusjs: - shortname: - siteName: - apikey: - api: - nocomment: # display when a blog post or an article has no comment attached - admin: - adminLabel: - -# livere (來必力) -# https://www.livere.com/ -livere: - uid: - -# gitalk -# https://github.com/gitalk/gitalk -gitalk: - client_id: - client_secret: - repo: - owner: - admin: - language: zh-CN # en, zh-CN, zh-TW, es-ES, fr, ru - perPage: 10 # Pagination size, with maximum 100. - distractionFreeMode: false # Facebook-like distraction free mode. - pagerDirection: last # Comment sorting direction, available values are last and first. - createIssueManually: false # Gitalk will create a corresponding github issue for your every single page automatically - -# valine -# https://valine.js.org -valine: - appId: c20JQYjgSVXdg1U7txRwJK6g-gzGzoHsz # leancloud application app id - appKey: xgkoVU56LqxWjGwRprTfoFSN # leancloud application app key - pageSize: 10 # comment list page size - avatar: monsterid # gravatar style https://valine.js.org/#/avatar - lang: zh-CN # i18n: zh-CN/zh-TW/en/ja - placeholder: Please leave your footprints # valine comment input placeholder (like: Please leave your footprints) - guest_info: nick,mail,link # valine comment header info (nick/mail/link) - recordIP: false # Record reviewer IP - serverURLs: # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in) - bg: # valine background - emojiCDN: # emoji CDN - enableQQ: false # enable the Nickname box to automatically get QQ Nickname and QQ Avatar - requiredFields: nick,mail # required fields (nick/mail) - option: - -# utterances -# https://utteranc.es/ -utterances: - repo: - # Issue Mapping: pathname/url/title/og:title - issue_term: pathname - # Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark - light_theme: github-light - dark_theme: photon-dark - -# Facebook Comments Plugin -# https://developers.facebook.com/docs/plugins/comments/ -facebook_comments: - app_id: - user_id: # optional - pageSize: 10 # The number of comments to show - order_by: social # social/time/reverse_time - lang: en_US # Language en_US/zh_CN/zh_TW and so on - -# Twikoo -# https://github.com/imaegoo/twikoo -twikoo: - -# Chat Services -# -------------------------------------- - -# Chat Button [recommend] -# It will create a button in the bottom right corner of website, and hide the origin button -chat_btn: false - -# The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down -chat_hide_show: false - -# chatra -# https://chatra.io/ -chatra: - enable: false - id: - -# tidio -# https://www.tidio.com/ -tidio: - enable: false - public_key: - -# daovoice -# http://daovoice.io/ -daovoice: - enable: false - app_id: - -# gitter -# https://gitter.im/ -gitter: - enable: false - room: - -# crisp -# https://crisp.chat/en/ -crisp: - enable: false - website_id: - -# Footer Settings -# -------------------------------------- -footer: - owner: - enable: true - since: 2018 - custom_text: - copyright: true # Copyright of theme and framework - ICP: # Chinese ICP License - enable: false - url: - text: - icon: /img/icp.png - -# Analysis -# -------------------------------------- - -# Baidu Analytics -# https://tongji.baidu.com/web/welcome/login -baidu_analytics: - -# Google Analytics -# https://analytics.google.com/analytics/web/ -google_analytics: - -# Tencent Analytics ID -# https://mta.qq.com -tencent_analytics: - -# CNZZ Analytics -# https://www.umeng.com/ -cnzz_analytics: - -# Advertisement -# -------------------------------------- - -# Google Adsense (谷歌廣告) -google_adsense: - enable: false - auto_ads: true - js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js - client: - enable_page_level_ads: true - -# Insert ads manually (手動插入廣告) -# ad: -# index: -# aside: -# post: - -# Verification (站長驗證) -# -------------------------------------- - -# Google Webmaster tools verification setting -# See: https://www.google.com/webmasters/ -google_site_verification: - -# Bing Webmaster tools verification setting -# See: https://www.bing.com/webmaster/ -bing_site_verification: - -# Baidu Webmaster tools verification setting -# See: https://ziyuan.baidu.com/site/ -baidu_site_verification: - -# 360 Webmaster tools verification setting -# see http://zhanzhang.so.com/ -qihu_site_verification: - -# Yandex Webmaster tools verification setting -# see https://webmaster.yandex.com/ -yandex_site_verification: - -# Beautify/Effect (美化/效果) -# -------------------------------------- - -# Theme color for customize -# Notice: color value must in double quotes like "#000" or may cause error! - -# theme_color: -# enable: true -# main: "#49B1F5" -# paginator: "#00c4b6" -# button_hover: "#FF7242" -# text_selection: "#00c4b6" -# link_color: "#99a9bf" -# meta_color: "#858585" -# hr_color: "#A4D8FA" -# code_foreground: "#F47466" -# code_background: "rgba(27, 31, 35, .05)" -# toc_color: "#00c4b6" -# blockquote_padding_color: "#49b1f5" -# blockquote_background_color: "#49b1f5" - -# The top_img settings of home page -# default: top img - full screen, site info - middle (默認top_img全屏,site_info在中間) -# The position of site info, eg: 300px/300em/300rem/10% (主頁標題距離頂部距離) -index_site_info_top: -# The height of top_img, eg: 300px/300em/300rem (主頁top_img高度) -index_top_img_height: - -# The user interface setting of category and tag page (category和tag頁的UI設置) -# index - same as Homepage UI (index 值代表 UI將與首頁的UI一樣) -# default - same as archives UI 默認跟archives頁面UI一樣 -category_ui: # 留空或 index -tag_ui: # 留空或 index - -# Website Background (設置網站背景) -# can set it to color or image (可設置圖片 或者 顔色) -# The formal of image: url(http://xxxxxx.com/xxx.jpg) -background: url(http://zfe.space/images/tianqizhizi.jpg) - -# Footer Background -footer_bg: false - -# the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px) -rightside-bottom: - -# Enter transitions (開啓網頁進入效果) -enter_transitions: true - -# Background effects (背景特效) -# -------------------------------------- - -# canvas_ribbon (靜止彩帶背景) -# See: https://github.com/hustcc/ribbon.js -canvas_ribbon: - enable: false - size: 150 - alpha: 0.6 - zIndex: -1 - click_to_change: true - mobile: true - -# Fluttering Ribbon (動態彩帶) -canvas_fluttering_ribbon: - enable: false - mobile: false - -# canvas_nest -# https://github.com/hustcc/canvas-nest.js -canvas_nest: - enable: false - color: '0,0,255' #color of lines, default: '0,0,0'; RGB values: (R,G,B).(note: use ',' to separate.) - opacity: 0.7 # the opacity of line (0~1), default: 0.5. - zIndex: -1 # z-index property of the background, default: -1. - count: 99 # the number of lines, default: 99. - mobile: false - -# Typewriter Effect (打字效果) -# https://github.com/disjukr/activate-power-mode -activate_power_mode: - enable: false - colorful: true # open particle animation (冒光特效) - shake: true # open shake (抖動特效) - mobile: false - -# Mouse click effects: fireworks (鼠標點擊效果: 煙火特效) -fireworks: - enable: false - zIndex: 9999 # -1 or 9999 - mobile: false - -# Mouse click effects: Heart symbol (鼠標點擊效果: 愛心) -click_heart: - enable: false - mobile: false - -# Mouse click effects: words (鼠標點擊效果: 文字) -ClickShowText: - enable: false - text: - # - I - # - LOVE - # - YOU - fontSize: 15px - mobile: false - -# Default display mode (網站默認的顯示模式) -# light (default) / dark -display_mode: light - -# Beautify (美化頁面顯示) -beautify: - enable: true - field: site # site/post - title-prefix-icon: '\f0c1' - title-prefix-icon-color: '#F47466' - -# Global font settings -# Don't modify the following settings unless you know how they work (非必要不要修改) -font: - global-font-size: - code-font-size: - font-family: - code-font-family: - -# Font settings for the site title and site subtitle -# 左上角網站名字 主頁居中網站名字 -blog_title_font: - font_link: - font-family: - -# The setting of divider icon (水平分隔線圖標設置) -hr_icon: - enable: true - icon: '\f21c' # the unicode value of Font Awesome icon, such as '\3423' - icon-top: -20px - -# the subtitle on homepage (主頁subtitle) -subtitle: - enable: true - # Typewriter Effect (打字效果) - effect: true - # loop (循環打字) - loop: true - # source調用第三方服務 - # source: false 關閉調用 - # source: 1 調用搏天api的隨機語錄(簡體) https://api.btstu.cn/ - # source: 2 調用一言網的一句話(簡體) https://hitokoto.cn/ - # source: 3 調用一句網(簡體) http://yijuzhan.com/ - # source: 4 調用今日詩詞(簡體) https://www.jinrishici.com/ - # subtitle 會先顯示 source , 再顯示 sub 的內容 - source: false - # 如果有英文逗號' , ',請使用轉義字元 , - # 如果有英文雙引號' " ',請使用轉義字元 " - # 開頭不允許轉義字元,如需要,請把整個句子用雙引號包住 - # 如果關閉打字效果,subtitle只會顯示sub的第一行文字 - sub: - - 日拱一卒无有尽,功不唐捐终入海 - - 首先应努力成为一个好程序员,入职一线互联网公司只是顺路的事 - -# Loading Animation (加載動畫) -preloader: false - -# aside (側邊欄) -# -------------------------------------- - -aside: - enable: true - hide: false - button: true - mobile: true # display on mobile - position: right # left or right - card_author: - enable: true - description: - button: - icon: fab fa-github - text: 请关注我 - link: https://github.com/LeeDebug - card_announcement: - enable: true - content: 以下为本人常活跃的社区,有兴趣的大佬可以给个关注,给个 Star 也好
    掘金入口
    语雀入口
    简书入口
    开源中国入口
    博客园入口 - card_recent_post: - enable: true - limit: 5 # if set 0 will show all - sort: date # date or updated - card_categories: - enable: true - limit: 8 # if set 0 will show all - expand: none # none/true/false - card_tags: - enable: true - limit: 40 # if set 0 will show all - color: true - card_archives: - enable: true - type: monthly # yearly or monthly - format: MMMM YYYY # eg: YYYY年MM月 - order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending - limit: 8 # if set 0 will show all - card_webinfo: - enable: true - post_count: true - last_push_date: true - -# busuanzi count for PV / UV in site -# 訪問人數 -busuanzi: - site_uv: true - site_pv: true - page_pv: true - -# Time difference between publish date and now (網頁運行時間) -# Formal: Month/Day/Year Time or Year/Month/Day Time -runtimeshow: - enable: true - publish_date: 9/12/2018 20:13:14 - -# Aside widget - Newest Comments -newest_comments: - enable: true - limit: 6 - avatar: true - leancloud: - enable: false - appId: c20JQYjgSVXdg1U7txRwJK6g-gzGzoHsz # leancloud application app id - appKey: xgkoVU56LqxWjGwRprTfoFSN # leancloud application app key - # serverURL: https://leedebug.github.io/ # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in) - serverURL: https://blog.leedebug.cn/ # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in) - default_avatar: wavatar # mp/identicon/monsterid/wavatar/retro/robohash/blank - github_issues: - enable: false - repo: - disqus: - enable: false - forum: - api_key: - -# Bottom right button (右下角按鈕) -# -------------------------------------- - -# Conversion between Traditional and Simplified Chinese (簡繁轉換) -translate: - enable: true - # The text of a button - default: 繁 - # the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese) - defaultEncoding: 2 - # Time delay - translateDelay: 0 - # The text of the button when the language is Simplified Chinese - msgToTraditionalChinese: '繁' - # The text of the button when the language is Traditional Chinese - msgToSimplifiedChinese: '简' - -# Read Mode (閲讀模式) -readmode: true - -# dark mode -darkmode: - enable: true - # Toggle Button to switch dark/light mode - button: true - # Switch dark/light mode automatically (自動切換 dark mode和 light mode) - # autoChangeMode: 1 Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am - # autoChangeMode: 2 Switch dark mode between 6 pm to 6 am - # autoChangeMode: false - autoChangeMode: 1 - -# Lightbox (圖片大圖查看模式) -# -------------------------------------- -# You can only choose one, or neither (只能選擇一個 或者 兩個都不選) - -# medium-zoom -# https://github.com/francoischalifour/medium-zoom -medium_zoom: false - -# fancybox -# http://fancyapps.com/fancybox/3/ -fancybox: true - -# Tag Plugins settings (標籤外掛) -# -------------------------------------- - -# mermaid -# see https://github.com/knsv/mermaid -mermaid: - enable: false - # built-in themes: default/forest/dark/neutral - theme: default - -# Note (Bootstrap Callout) -note: - # Note tag style values: - # - simple bs-callout old alert style. Default. - # - modern bs-callout new (v2-v3) alert style. - # - flat flat callout style with background, like on Mozilla or StackOverflow. - # - disabled disable all CSS styles import of note tag. - style: flat - icons: true - border_radius: 3 - # Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6). - # Offset also applied to label tag variables. This option can work with disabled note tag. - light_bg_offset: 0 - -# other -# -------------------------------------- - -# Artitalk -# see https://artitalk.js.org/ -artitalk: - appId: - appKey: - option: - -# Pjax [Beta] -# It may contain bugs and unstable, give feedback when you find the bugs. -# https://github.com/MoOx/pjax -pjax: - enable: false - exclude: - # - xxxx - # - xxxx - -# Inject the css and script (aplayer/meting) -aplayerInject: - enable: false - per_page: true - -# Snackbar (Toast Notification 彈窗) -# https://github.com/polonel/SnackBar -# position 彈窗位置 -# 可選 top-left / top-center / top-right / bottom-left / bottom-center / bottom-right -snackbar: - enable: true - position: top-right - bg_light: '#49b1f5' # The background color of Toast Notification in light mode - bg_dark: '#121212' # The background color of Toast Notification in dark mode - -# Baidu Push (百度推送) -baidu_push: false - -# https://instant.page/ -# prefetch (預加載) -instantpage: - enable: true - -# https://github.com/vinta/pangu.js -# Insert a space between Chinese character and English character (中英文之間添加空格) -pangu: - enable: false - field: site # site/post - -# Lazyload (圖片懶加載) -# https://github.com/verlok/lazyload -lazyload: - enable: true - post: /img/loading.gif - -# PWA -# See https://github.com/JLHwung/hexo-offline -# --------------- -# pwa: -# enable: false -# manifest: /image/pwa/manifest.json -# apple_touch_icon: /image/pwa/apple-touch-icon.png -# favicon_32_32: /image/pwa/32.png -# favicon_16_16: /image/pwa/16.png -# mask_icon: /image/pwa/safari-pinned-tab.svg - -# Disable Baidu transformation on mobile devices (禁止百度轉碼) -disable_baidu_transformation: true - -# Open graph meta tags -# https://developers.facebook.com/docs/sharing/webmasters/ -Open_Graph_meta: true - -# Caches the contents in a fragment, speed up the generation (開啟hexo自帶的緩存,加快生成速度) -fragment_cache: true - -# Inject -# Insert the code to head (before '' tag) and the bottom (before '' tag) -# 插入代码到头部 之前 和 底部 之前 -inject: - head: - - - - - # - - bottom: - - - - - -# CDN -# Don't modify the following settings unless you know how they work -# 非必要請不要修改 -CDN: - # main - main_css: /css/index.css - jquery: https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js - main: /js/main.js - utils: /js/utils.js - - # pjax - pjax: https://cdn.jsdelivr.net/npm/pjax/pjax.min.js - - # comments - gitalk: https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js - gitalk_css: https://cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.css - valine: https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js - disqusjs: https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqus.js - disqusjs_css: https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqusjs.css - utterances: https://utteranc.es/client.js - twikoo: https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.js - - # share - addtoany: https://static.addtoany.com/menu/page.js - sharejs: https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js - sharejs_css: https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css - - # search - local_search: /js/search/local-search.js - algolia_js: /js/search/algolia.js - algolia_search: https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.js - algolia_search_css: https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.css - - # math - mathjax: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js - katex: https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css - katex_copytex: https://cdn.jsdelivr.net/npm/katex-copytex@latest/dist/katex-copytex.min.js - katex_copytex_css: https://cdn.jsdelivr.net/npm/katex-copytex@latest/dist/katex-copytex.min.css - mermaid: https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js - - # count - busuanzi: //busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js - - # background effect - canvas_ribbon: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js - canvas_fluttering_ribbon: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js - canvas_nest: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js - - lazyload: https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js - instantpage: https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js - typed: https://cdn.jsdelivr.net/npm/typed.js/lib/typed.min.js - pangu: https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js - - # photo - fancybox_css: https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css - fancybox: https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js - medium_zoom: https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js - - # snackbar - snackbar_css: https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css - snackbar: https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js - - # effect - activate_power_mode: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js - fireworks: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js - click_heart: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js - ClickShowText: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js - - # fontawesome - # 图标链接:https://fontawesome.com/icons - # 图标链接:http://www.fontawesome.com.cn/faicons/ - # 可用部分:网页常用图标 (Web Application Icons) - fontawesome: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css - - # Conversion between Traditional and Simplified Chinese - translate: /js/tw_cn.js - - # justifiedGallery - justifiedGallery_js: https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js - justifiedGallery_css: https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css - - # aplayer - aplayer_css: https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css - aplayer_js: https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js - meting_js: https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js - - # Prism.js - prismjs_js: https://cdn.jsdelivr.net/npm/prismjs/prism.min.js - prismjs_lineNumber_js: https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js - prismjs_autoloader: https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js - - artitalk: https://cdn.jsdelivr.net/npm/artitalk diff --git a/themes/hexo-theme-butterfly-3.3.0/languages/default.yml b/themes/hexo-theme-butterfly-3.3.0/languages/default.yml deleted file mode 100755 index d1699c34c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/languages/default.yml +++ /dev/null @@ -1,114 +0,0 @@ -footer: - framework: Framework - theme: Theme - -copy: - success: Copy successfully - error: Copy error - noSupport: The browser does not support - -page: - articles: Articles - tag: Tag - category: Category - archives: Archives - -sticky: Sticky -no_title: No title - -post: - created: Created - updated: Updated - wordcount: Word count - min2read: Reading time - min2read_unit: min - page_pv: Post View - comments: Comments - copyright: - author: Author - link: Link - copyright_notice: Copyright Notice - copyright_content: 'All articles in this blog are licensed under %s unless stating additionally.' - recommend: Related Articles - -search: Search -algolia_search: - input_placeholder: Search for Posts - hits_empty: "We didn't find any results for the search: ${query}." - hits_stats: '${hits} results found in ${time} ms' - -local_search: - label: Local search - input_placeholder: Search for Posts - hits_empty: "We didn't find any results for the search: ${query}" - powered_by: Powered by - -pagination: - prev: Previous Post - next: Next Post - -comment: Comment - -aside: - articles: Articles - tags: Tags - categories: Categories - card_announcement: Announcement - card_categories: Categories - card_tags: Tags - card_archives: Archives - card_recent_post: Recent Post - card_webinfo: - headline: Info - article_name: Article - runtime: - name: Run time - unit: days - last_push_date: - name: Last Push - site_wordcount: Total Count - site_uv_name: UV - site_pv_name: PV - more_button: More - card_newest_comments: - headline: Newest Comments - loading_text: loading... - error: Unable to get the data, please make sure the settings are correct. - card_toc: Catalog - -date_suffix: - just: Just - min: minutes ago - hour: hours ago - day: days ago - month: months ago - -donate: Donate -share: Share - -rightside: - readmode_title: Read Mode - translate_title: Switch Between Traditional Chinese And Simplified Chinese - night_mode_title: Switch Between Light And Dark Mode - back_to_top: Back To Top - toc: Table Of Contents - scroll_to_comment: Scroll To Comments - setting: Setting - -copy_copyright: - author: Author - link: Link - source: Source - info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source. - -Snackbar: - chs_to_cht: Traditional Chinese Activated Manually - cht_to_chs: Simplified Chinese Activated Manually - day_to_night: Dark Mode Activated Manually - night_to_day: Light Mode Activated Manually - -loading: Loading... - -error404: - error_title: Page not found - back_button: Go back home diff --git a/themes/hexo-theme-butterfly-3.3.0/languages/en.yml b/themes/hexo-theme-butterfly-3.3.0/languages/en.yml deleted file mode 100755 index 0307a59a4..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/languages/en.yml +++ /dev/null @@ -1,116 +0,0 @@ -footer: - framework: 框架 - theme: 主题 - -copy: - success: 复制成功 - error: 复制失败 - noSupport: 该浏览器不支持复制功能 - -page: - articles: 文章 - tag: 标签 - category: 分类 - archives: 时间轴 - -sticky: Sticky -no_title: No title - -post: - created: 发表于 - updated: 更新于 - wordcount: 文章字数 - min2read: 预计阅读时间 - min2read_unit: 分钟 - page_pv: 阅读量 - comments: 评论 - copyright: - author: 作者 - link: 链接 - copyright_notice: Copyright Notice - copyright_content: 'All articles in this blog are licensed under %s unless stating additionally.' - recommend: 推荐文章 - -search: 搜索 -algolia_search: - input_placeholder: Search for Posts - hits_empty: "We didn't find any results for the search: ${query}." - hits_stats: '${hits} results found in ${time} ms' - -local_search: - label: Local search - input_placeholder: Search for Posts - hits_empty: "We didn't find any results for the search: ${query}" - powered_by: Powered by - -pagination: - prev: 上一篇 - next: 下一篇 - -comment: 评论 - -aside: - articles: 文章 - tags: 标签 - categories: 分类 - card_announcement: 公告 - card_categories: 分类 - card_tags: 标签 - card_archives: 时间轴 - card_recent_post: 最近文章 - card_webinfo: - headline: 网展资讯 - article_name: 文章数目 - runtime: - name: 已运行时间 - unit: 天 - last_push_date: - name: 最后发表时间 - site_wordcount: 本站总字数 - site_uv_name: 本站访客数 - site_pv_name: 本站总访问量 - more_button: 查看更多 - card_newest_comments: - headline: 最新评论 - loading_text: 正在努力加载中... - error: Unable to get the data, please make sure the settings are correct. - card_toc: 本文目录 - -date_suffix: - just: 刚刚 - min: 分钟以前 - hour: 小时以前 - day: 天以前 - month: 个月以前 - -donate: 捐赠 -share: 分享 - -rightside: - readmode_title: 阅读模式 - font_plus_title: 放大字体 - font_minus_title: 缩小字体 - translate_title: 简体/繁体 - night_mode_title: 开灯/关灯 - back_to_top: 回到顶部 - toc: 目录 - scroll_to_comment: 前往评论 - setting: 设置 - -copy_copyright: - author: 作者 - link: 链接 - source: Source - info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source. - -Snackbar: - chs_to_cht: 已切换至繁体中文 - cht_to_chs: 已切换至简体中文 - day_to_night: 已切换至夜间模式 - night_to_day: 已切换至日间模式 - -loading: 正在努力加载中... - -error404: - error_title: 页面未找到,请联系站点管理人员 - back_button: 点击返回首页 diff --git a/themes/hexo-theme-butterfly-3.3.0/languages/zh-CN.yml b/themes/hexo-theme-butterfly-3.3.0/languages/zh-CN.yml deleted file mode 100755 index 2a1a76264..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/languages/zh-CN.yml +++ /dev/null @@ -1,116 +0,0 @@ -footer: - framework: 框架 - theme: 主题 - -copy: - success: 复制成功 - error: 复制错误 - noSupport: 浏览器不支持 - -page: - articles: 文章总览 - tag: 标签 - category: 分类 - archives: 归档 - -sticky: 置顶 -no_title: 无题 - -post: - created: 发表于 - updated: 更新于 - wordcount: 字数总计 - min2read: 阅读时长 - min2read_unit: 分钟 - page_pv: 阅读量 - comments: 评论数 - copyright: - author: 文章作者 - link: 文章链接 - copyright_notice: 版权声明 - copyright_content: '本博客所有文章除特别声明外,均采用 - %s 许可协议。转载请注明来自 %s!' - recommend: 相关推荐 - -search: 搜索 -algolia_search: - input_placeholder: 搜索文章 - hits_empty: '找不到您查询的内容:${query}' - hits_stats: '找到 ${hits} 条结果,用时 ${time} 毫秒' - -local_search: - label: 本地搜索 - input_placeholder: 搜索文章 - hits_empty: '找不到您查询的内容:${query}' - powered: '提供支持' - by: 由 - -pagination: - prev: 上一篇 - next: 下一篇 - -comment: 评论 - -aside: - articles: 文章 - tags: 标签 - categories: 分类 - card_announcement: 公告 - card_categories: 分类 - card_tags: 标签 - card_archives: 归档 - card_recent_post: 最新文章 - card_webinfo: - headline: 网站资讯 - article_name: 文章数目 - runtime: - name: 已运行时间 - unit: 天 - last_push_date: - name: 最后更新时间 - site_wordcount: 本站总字数 - site_uv_name: 本站访客数 - site_pv_name: 本站总访问量 - more_button: 查看更多 - card_newest_comments: - headline: 最新评论 - loading_text: 正在加载中... - error: 无法获取资料,请确认相关配置是否正确 - card_toc: 目录 - -date_suffix: - just: 刚刚 - min: 分钟前 - hour: 小时前 - day: 天前 - month: 个月前 - -donate: 打赏 -share: 分享 - -rightside: - readmode_title: 阅读模式 - translate_title: 简繁转换 - night_mode_title: 浅色和深色模式转换 - back_to_top: 回到顶部 - toc: 目录 - scroll_to_comment: 直达评论 - setting: 设置 - -copy_copyright: - author: 作者 - link: 链接 - source: 来源 - info: 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 - -Snackbar: - chs_to_cht: 你已切换为繁体 - cht_to_chs: 你已切换为简体 - day_to_night: 你已切换为深色模式 - night_to_day: 你已切换为浅色模式 - -loading: 加载中... - -error404: - error_title: 页面没有找到 - back_button: 回到主页 diff --git a/themes/hexo-theme-butterfly-3.3.0/languages/zh-TW.yml b/themes/hexo-theme-butterfly-3.3.0/languages/zh-TW.yml deleted file mode 100755 index 2b30d2a9b..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/languages/zh-TW.yml +++ /dev/null @@ -1,116 +0,0 @@ -footer: - framework: 框架 - theme: 主題 - -copy: - success: 複製成功 - error: 複製錯誤 - noSupport: 瀏覽器不支援 - -page: - articles: 文章總覽 - tag: 標籤 - category: 分類 - archives: 歸檔 - -sticky: 置頂 -no_title: 無題 - -post: - created: 發表於 - updated: 更新於 - wordcount: 字數總計 - min2read: 閲讀時長 - min2read_unit: 分鐘 - page_pv: 閱讀量 - comments: 評論數 - copyright: - author: 文章作者 - link: 文章連結 - copyright_notice: 版權聲明 - copyright_content: '本部落格所有文章除特別聲明外,均採用 - %s 許可協議。轉載請註明來自 %s!' - recommend: 相關推薦 - -search: 搜尋 -algolia_search: - input_placeholder: 搜尋文章 - hits_empty: '找不到您查詢的內容:${query}' - hits_stats: '找到 ${hits} 條結果,用時 ${time} 毫秒' - -local_search: - label: 本地搜尋 - input_placeholder: 搜尋文章 - hits_empty: '找不到您查詢的內容:${query}' - powered: '提供支援' - by: 由 - -pagination: - prev: 上一篇 - next: 下一篇 - -comment: 評論 - -aside: - articles: 文章 - tags: 標籤 - categories: 分類 - card_announcement: 公告 - card_categories: 分類 - card_tags: 標籤 - card_archives: 歸檔 - card_recent_post: 最新文章 - card_webinfo: - headline: 網站資訊 - article_name: 文章數目 - runtime: - name: 已運行時間 - unit: 天 - last_push_date: - name: 最後更新時間 - site_wordcount: 本站總字數 - site_uv_name: 本站訪客數 - site_pv_name: 本站總訪問量 - more_button: 檢視更多 - card_newest_comments: - headline: 最新評論 - loading_text: 正在加載中... - error: 無法獲取資料,請確認相關配置是否正確 - card_toc: 目錄 - -date_suffix: - just: 剛剛 - min: 分鐘前 - hour: 小時前 - day: 天前 - month: 個月前 - -donate: 打賞 -share: 分享 - -rightside: - readmode_title: 閱讀模式 - translate_title: 簡繁轉換 - night_mode_title: 淺色和深色模式轉換 - back_to_top: 回到頂部 - toc: 目錄 - scroll_to_comment: 直達評論 - setting: 設定 - -copy_copyright: - author: 作者 - link: 連結 - source: 來源 - info: 著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。 - -Snackbar: - chs_to_cht: 你已切換為繁體 - cht_to_chs: 你已切換為簡體 - day_to_night: 你已切換為深色模式 - night_to_day: 你已切換為淺色模式 - -loading: 載入中... - -error404: - error_title: 頁面沒有找到 - back_button: 回到主頁 diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/404.pug b/themes/hexo-theme-butterfly-3.3.0/layout/404.pug deleted file mode 100755 index 364b7cf16..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/404.pug +++ /dev/null @@ -1,28 +0,0 @@ -- var top_img = theme.error_404.background || theme.default_top_img -- var bg_img = `background-image: url(${url_for(top_img)})` - -doctype html -html(lang=config.language data-theme=theme.display_mode) - head - include includes/head.pug - body - if theme.preloader - !=partial('includes/loading/loading', {}, {cache:theme.fragment_cache}) - - if theme.fireworks && theme.fireworks.enable - canvas.fireworks - - if theme.background - #web_bg - - #error-wrap - .error-content - .error-img(style=bg_img) - .error-info - h1.error_title= '404' - .error_subtitle= theme.error_404.subtitle - a.button--animated(href=config.root) - i.fas.fa-rocket - = _p('error404.back_button') - - include includes/additional-js.pug diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/archive.pug b/themes/hexo-theme-butterfly-3.3.0/layout/archive.pug deleted file mode 100755 index faa485c10..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/archive.pug +++ /dev/null @@ -1,8 +0,0 @@ -extends includes/layout.pug - -block content - include ./includes/mixins/article-sort.pug - #archive - .article-sort-title= _p('page.articles') + ' - ' + site.posts.length - +articleSort(page.posts) - include includes/pagination.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/category.pug b/themes/hexo-theme-butterfly-3.3.0/layout/category.pug deleted file mode 100755 index 01ad99042..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/category.pug +++ /dev/null @@ -1,14 +0,0 @@ -extends includes/layout.pug - -block content - if theme.category_ui == 'index' - include ./includes/mixins/post-ui.pug - #recent-posts.recent-posts.category_ui - +postUI - include includes/pagination.pug - else - include ./includes/mixins/article-sort.pug - #category - .article-sort-title= _p('page.category') + ' - ' + page.category - +articleSort(page.posts) - include includes/pagination.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/gitcalendar.pug b/themes/hexo-theme-butterfly-3.3.0/layout/gitcalendar.pug deleted file mode 100644 index 12a71f81c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/gitcalendar.pug +++ /dev/null @@ -1,45 +0,0 @@ -#calendar.calendar - #gitmessage(:style='{top:y+px,left:x+px,position: fixed,zIndex:9999}') - .angle-wrapper - span {{span1}}   - span {{span2}} 次提交 - .position-relative - .border.py-2.graph-before-activity-overview - .js-calendar-graph.mx-md-2.mx-3.d-flex.flex-column.flex-items-end.flex-xl-items-center.overflow-hidden.pt-1.is-graph-loading.graph-canvas.calendar-graph.height-full.text-center(data-graph-url='/users/LeeDebug/contributions?to=2020-10-29', data-url='/LeeDebug', data-from='2019-10-27 00:00:00 UTC', data-to='2020-10-29 23:59:59 UTC', data-org) - #calendarcanvasbox(v-if='simplemode') - canvas#gitcanvas(style='animation: none;') - svg.js-calendar-graph-svg(width='100%', viewBox='0 0 770 128', v-if='!simplemode') - text.month(:x='32 + monthindex*64', y='20', v-for='(month,monthindex) in monthchange') {{month}} - text.wday(text-anchor='start', dx='0', dy='40') 日 - text.wday(text-anchor='start', dx='0', dy='65') 二 - text.wday(text-anchor='start', dx='0', dy='90') 四 - text.wday(text-anchor='start', dx='0', dy='115') 六 - g(v-for='(weekitem,weekIndex) in data', :transform='\'translate(\'+ (16 + weekIndex*14) + \',\' + \'0)\'') - rect(@mouseover="selectStyle(dayitem,$event)" @mouseleave="outStyle()" v-for='(dayitem,dayIndex) in weekitem', :style='{fill:thiscolor(dayitem.count),shapeRendering:crispedges}', :data-score='dayitem.count', :data-date='dayitem.date', x='0', :y=' 30 + dayIndex*13 ', width='11', height='11') - .contrib-footer.clearfix.mt-1.mx-3.px-3.pb-1 - .float-left.text-gray - | 数据来源 - a(:href="'https://github.com/'+ user ", target='blank') @{{user}} - .contrib-legend.text-gray(title='A summary of pull requests, issues opened, and commits to the default and gh-pages branches.') - | Less - - ul.legend - li(:style='{backgroundColor:leeGreen[0]}') - li(:style='{backgroundColor:leeGreen[1]}') - li(:style='{backgroundColor:leeGreen[2]}') - li(:style='{backgroundColor:leeGreen[3]}') - li(:style='{backgroundColor:leeGreen[4]}') - | More - - .contrib-column.contrib-column-first.table-column - span.text-muted 过去一年提交 - span.contrib-number {{total}} - span.text-muted.data-range {{oneyearbeforeday}} - {{thisday}} - .contrib-column.table-column - span.text-muted 最近一月提交 - span.contrib-number {{thisweekdatacore}} - span.text-muted.data-range {{amonthago}} - {{thisday}} - .contrib-column.table-column - span.text-muted 最近一周提交 - span.contrib-number {{weekdatacore}} - span.text-muted.data-range {{aweekago}} - {{thisday}} diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/additional-js.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/additional-js.pug deleted file mode 100755 index 8d912b7d2..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/additional-js.pug +++ /dev/null @@ -1,74 +0,0 @@ -div - script(src=url_for(theme.CDN.jquery)) - script(src=url_for(theme.CDN.utils)) - script(src=url_for(theme.CDN.main)) - - if theme.translate && theme.translate.enable - script(src=url_for(theme.CDN.translate)) - - if theme.medium_zoom - script(src=url_for(theme.CDN.medium_zoom)) - else if theme.fancybox - script(src=url_for(theme.CDN.fancybox)) - - if theme.instantpage - script(src=url_for(theme.CDN.instantpage) type="module" defer) - - if theme.lazyload.enable - script(src=url_for(theme.CDN.lazyload)) - - if (theme.snackbar && theme.snackbar.enable) - script(src=url_for(theme.CDN.snackbar)) - - if theme.pangu && theme.pangu.enable - !=partial('includes/third-party/pangu.pug', {}, {cache:theme.fragment_cache}) - - //- search - if theme.algolia_search.enable - script(src=url_for(theme.CDN.algolia_js)) - else if theme.local_search.enable - script(src=url_for(theme.CDN.local_search)) - - if theme.preloader - !=partial('includes/loading/loading-js', {}, {cache:theme.fragment_cache}) - - .js-pjax - if theme.subtitle.enable && is_home() && theme.index_img !== false - include ./third-party/subtitle.pug - - if page.type === 'artitalk' - include ./third-party/artitalk.pug - - include ./third-party/math/index.pug - - if commentsJsLoad - include ./third-party/comments/js.pug - - if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv - script(async src=url_for(theme.CDN.busuanzi)) - - !=partial('includes/third-party/prismjs', {}, {cache:theme.fragment_cache}) - - if theme.aside.enable && theme.newest_comments.enable - if theme.pjax.enable - !=partial('includes/third-party/newest-comments/index', {}, {cache:theme.fragment_cache}) - else if (!is_post() && page.aside !== false) - !=partial('includes/third-party/newest-comments/index', {}, {cache:theme.fragment_cache}) - - !=fragment_cache('injectBottom', function(){return injectHtml(theme.inject.bottom)}) - - !=partial('includes/third-party/effect', {}, {cache:theme.fragment_cache}) - - !=partial('includes/third-party/chat/index', {}, {cache:theme.fragment_cache}) - - if theme.aplayerInject && theme.aplayerInject.enable - if theme.pjax.enable || theme.aplayerInject.per_page - include ./third-party/aplayer.pug - else if page.aplayer - include ./third-party/aplayer.pug - - if theme.pjax.enable - !=partial('includes/third-party/pjax', {}, {cache:theme.fragment_cache}) - - !=partial('includes/third-party/baidu_push', {}, {cache:theme.fragment_cache}) - \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/contributions.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/contributions.pug deleted file mode 100644 index 58f39b93f..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/contributions.pug +++ /dev/null @@ -1,8 +0,0 @@ -//- 默认的贡献墙图片 -//- .recent-post-item(style="width:100%;height:auto;") -//- img(src='https://ghchart.rshah.org/LeeDebug', alt style="width:100%;height:100%;padding-left: 2em;padding-right: 2em;padding-top: 1.2em;padding-bottom: 1.5em;") - -//- 修改后的贡献墙图片 -.recent-post-item(style="width:100%;height:auto;") - div#calendar.calendar - div.position-relative \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/footer.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/footer.pug deleted file mode 100755 index fc612f995..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/footer.pug +++ /dev/null @@ -1,23 +0,0 @@ -#footer-wrap - if theme.footer.owner.enable - - var now = new Date() - - var nowYear = now.getFullYear() - if theme.footer.owner.since && theme.footer.owner.since != nowYear - .copyright!= `©${theme.footer.owner.since} - ${nowYear} By ${config.author}` - else - .copyright!= `©${nowYear} By ${config.author}` - if theme.footer.copyright - .framework-info - span= _p('footer.framework') + ' ' - a(href='https://hexo.io')= 'Hexo' - span.footer-separator | - span= _p('footer.theme') + ' ' - a(href='https://github.com/jerryc127/hexo-theme-butterfly')= 'Butterfly' - if theme.footer.custom_text - .footer_custom_text!=`${theme.footer.custom_text}` - if theme.footer.ICP.enable - .icp - a(href=theme.footer.ICP.url) - if theme.footer.ICP.icon - img.icp-icon(src=url_for(theme.footer.ICP.icon) alt='ICP') - span=theme.footer.ICP.text diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head.pug deleted file mode 100755 index 0432ee02c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head.pug +++ /dev/null @@ -1,87 +0,0 @@ -- var pageTitle -- if (is_archive()) pageTitle = _p('page.archives') -- else if (is_tag()) pageTitle = _p('page.tag') + ': ' + page.tag -- else if (is_category()) pageTitle = _p('page.category') + ': ' + page.category -- else if (is_month()) pageTitle += ': ' + page.month + '/' + page.year -- else if (is_year()) pageTitle += ': ' + page.year -- else if (is_current('/404.html', [strict])) pageTitle = _p('error404.error_title') -- else pageTitle = page.title || config.title || '' - -- var isSubtitle = config.subtitle ? ' - ' + config.subtitle : '' -- var tabTitle = is_home() || !pageTitle ? config.title + isSubtitle : pageTitle + ' | ' + config.title - -- var pageKeywords -- if (page.keywords) pageKeywords = Array.isArray(page.keywords) ? (page.keywords).join(',') : ([]).join(',') || page.keywords -- else if (page.tags && page.tags.length) pageKeywords = page.tags.data.map(function(tag) {return tag.name;}).join(',') -- else pageKeywords = Array.isArray(config.keywords) ? (config.keywords).join(','): ([]).join(',') || config.keywords -- var pageAuthor = config.email ? config.author + ',' + config.email : config.author -- var pageCopyright = config.copyright || config.author -- var themeColor = theme.display_mode === 'dark' ? '#0d0d0d' : '#ffffff' - -meta(charset='UTF-8') -meta(http-equiv="X-UA-Compatible" content="IE=edge") -meta(name="viewport" content="width=device-width,initial-scale=1") -title= tabTitle -if pageKeywords - meta(name="keywords" content=pageKeywords) -meta(name="author" content=pageAuthor) -meta(name="copyright" content=pageCopyright) -meta(name ="format-detection" content="telephone=no") -meta(name="theme-color" content=themeColor) - -if theme.disable_baidu_transformation - meta(http-equiv="Cache-Control" content="no-transform") - meta(http-equiv="Cache-Control" content="no-siteapp") - -//- 百度 HTML标签验证 -meta(name="baidu-site-verification" content="code-rAKVnGRDWr") - -//- Open_Graph -include ./head/Open_Graph.pug - -!=favicon_tag(theme.favicon || config.favicon) -link(rel="canonical" href=urlNoIndex()) - -//- 預解析 -!=partial('includes/head/preconnect', {}, {cache:theme.fragment_cache}) - -//- 網站驗證 -!=partial('includes/head/site_verification', {}, {cache:theme.fragment_cache}) - -//- PWA -if (theme.pwa && theme.pwa.enable) - !=partial('includes/head/pwa', {}, {cache:theme.fragment_cache}) - -//- main css -link(rel='stylesheet', href=url_for(theme.CDN.main_css)) -link(rel='stylesheet', href=url_for(theme.CDN.fontawesome)) - -if theme.fancybox - link(rel='stylesheet', href=url_for(theme.CDN.fancybox_css)) - -if (theme.snackbar && theme.snackbar.enable) - link(rel='stylesheet', href=url_for(theme.CDN.snackbar_css)) - -if theme.algolia_search.enable - link(rel='stylesheet' href=url_for(theme.CDN.algolia_search_css)) - script(src=url_for(theme.CDN.algolia_search) defer) - -//- google_adsense -!=partial('includes/head/google_adsense', {}, {cache:theme.fragment_cache}) - -//- analytics -!=partial('includes/head/analytics', {}, {cache:theme.fragment_cache}) - -//- font -if theme.blog_title_font && theme.blog_title_font.font_link - link(rel='stylesheet' href=url_for(theme.blog_title_font.font_link)) - -//- global config -!=partial('includes/head/config', {}, {cache:theme.fragment_cache}) - -include ./head/config_site.pug -include ./head/noscript.pug - -!=partial('includes/head/js', {}, {cache:theme.fragment_cache}) - -!=fragment_cache('injectHead', function(){return injectHtml(theme.inject.head)}) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/Open_Graph.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/Open_Graph.pug deleted file mode 100755 index 7d5c59ca3..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/Open_Graph.pug +++ /dev/null @@ -1,11 +0,0 @@ -if theme.Open_Graph_meta - - let contentType = is_post() ? 'article' : 'website' - - let metaImage = full_url_for(page.cover || theme.avatar.img) - - let fb_appId = theme.facebook_comments.app_id || '' - - let fb_admins = theme.facebook_comments.user_id || '' - - != open_graph({type: contentType, image: metaImage, fb_admins: fb_admins, fb_app_id: fb_appId}) - -else - meta(name="description" content=page_description()) - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/analytics.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/analytics.pug deleted file mode 100755 index f4379b56e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/analytics.pug +++ /dev/null @@ -1,32 +0,0 @@ -if theme.baidu_analytics - script. - var _hmt = _hmt || []; - (function() { - var hm = document.createElement("script"); - hm.src = "https://hm.baidu.com/hm.js?!{theme.baidu_analytics}"; - var s = document.getElementsByTagName("script")[0]; - s.parentNode.insertBefore(hm, s); - })(); - -if theme.google_analytics - script(async src=`https://www.googletagmanager.com/gtag/js?id=${theme.google_analytics}`) - script. - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', '!{theme.google_analytics}'); - -if theme.tencent_analytics - script. - var _mtac = {}; - (function() { - var mta = document.createElement("script"); - mta.src = "//pingjs.qq.com/h5/stats.js?v2.0.4"; - mta.setAttribute("name", "MTAH5"); - mta.setAttribute("sid", "!{theme.tencent_analytics}"); - var s = document.getElementsByTagName("script")[0]; - s.parentNode.insertBefore(mta, s); - })(); - -if theme.cnzz_analytics - script(async data-pjax src=`https://s4.cnzz.com/z_stat.php?id=${theme.cnzz_analytics}&web_id=${theme.cnzz_analytics}`) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config.pug deleted file mode 100755 index 99e3001fe..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config.pug +++ /dev/null @@ -1,160 +0,0 @@ -- - let algolia = 'undefined'; - let env = process.env; - if (theme.algolia_search.enable) { - algolia = JSON.stringify({ - appId: env.ALGOLIA_APP_ID || config.algolia.appId || config.algolia.applicationID, - apiKey: env.ALGOLIA_API_KEY || config.algolia.apiKey, - indexName: env.ALGOLIA_INDEX_NAME || config.algolia.indexName, - hits: theme.algolia_search.hits, - // search languages - languages: { - input_placeholder: _p("algolia_search.input_placeholder"), - hits_empty: _p("algolia_search.hits_empty"), - hits_stats: _p("algolia_search.hits_stats") - } - }) - } - - let localSearch = 'undefined'; - if (theme.local_search && theme.local_search.enable) { - localSearch = JSON.stringify({ - path: config.search.path, - languages: { - // search languages - hits_empty: _p("local_search.hits_empty") - } - }) - } - - let translate = 'undefined'; - if (theme.translate && theme.translate.enable){ - translate = JSON.stringify({ - defaultEncoding: theme.translate.defaultEncoding, - translateDelay: theme.translate.translateDelay, - msgToTraditionalChinese: theme.translate.msgToTraditionalChinese, - msgToSimplifiedChinese: theme.translate.msgToSimplifiedChinese - }) - } - - let copyright = 'undefined'; - if (theme.copy.enable && theme.copy.copyright.enable){ - copyright = JSON.stringify({ - limitCount: theme.copy.copyright.limit_count, - languages: { - author: _p("copy_copyright.author") + ': ' + config.author, - link: _p("copy_copyright.link") + ': ', - source: _p("copy_copyright.source") + ': ' + config.title, - info: _p("copy_copyright.info") - } - }) - } - - let ClickShowText = 'undefined'; - if (theme.ClickShowText && theme.ClickShowText.enable) { - ClickShowText = JSON.stringify({ - text: theme.ClickShowText.text.join(","), - fontSize: theme.ClickShowText.fontSize - }) - } - - let Snackbar = 'undefined'; - if (theme.snackbar && theme.snackbar.enable) { - Snackbar = JSON.stringify({ - chs_to_cht: _p("Snackbar.chs_to_cht"), - cht_to_chs: _p("Snackbar.cht_to_chs"), - day_to_night: _p("Snackbar.day_to_night"), - night_to_day: _p("Snackbar.night_to_day"), - bgLight: theme.snackbar.bg_light, - bgDark: theme.snackbar.bg_dark, - position: theme.snackbar.position, - }) - } - - let noticeOutdate = 'undefined'; - if (theme.noticeOutdate && theme.noticeOutdate.enable) { - noticeOutdate = JSON.stringify({ - limitDay: theme.noticeOutdate.limit_day, - position: theme.noticeOutdate.position, - messagePrev: theme.noticeOutdate.message_prev, - messageNext: theme.noticeOutdate.message_next, - }) - } - - let highlight = 'undefined'; - if ((config.highlight && config.highlight.enable) || (config.prismjs && config.prismjs.enable)) { - highlight = JSON.stringify({ - plugin: config.highlight.enable ? 'highlighjs' : 'prismjs', - highlightCopy: theme.highlight_copy, - highlightLang: theme.highlight_lang - }) - } - -script. - var GLOBAL_CONFIG = { - root: '!{config.root}', - algolia: !{algolia}, - localSearch: !{localSearch}, - translate: !{translate}, - noticeOutdate: !{noticeOutdate}, - highlight: !{highlight}, - copy: { - success: '!{_p("copy.success")}', - error: '!{_p("copy.error")}', - noSupport: '!{_p("copy.noSupport")}' - }, - relativeDate: { - homepage: !{theme.post_meta.page.date_format === 'relative'}, - post: !{theme.post_meta.post.date_format === 'relative'} - }, - runtime: '!{theme.runtimeshow.enable ? _p("aside.card_webinfo.runtime.unit") : ""}', - date_suffix: { - just: '!{_p("date_suffix.just")}', - min: '!{_p("date_suffix.min")}', - hour: '!{_p("date_suffix.hour")}', - day: '!{_p("date_suffix.day")}', - month: '!{_p("date_suffix.month")}' - }, - copyright: !{copyright}, - ClickShowText: !{ClickShowText}, - lightbox: '!{ theme.medium_zoom ? "mediumZoom" : (theme.fancybox ? "fancybox" : "null" )}', - Snackbar: !{Snackbar}, - justifiedGallery: { - js: '!{theme.CDN.justifiedGallery_js}', - css: '!{theme.CDN.justifiedGallery_css}' - }, - isPhotoFigcaption: !{theme.photofigcaption}, - islazyload: !{theme.lazyload.enable}, - isanchor: !{theme.anchor} - }; - - var saveToLocal = { - set: function setWithExpiry(key, value, ttl) { - const now = new Date() - const expiryDay = ttl * 86400000 - const item = { - value: value, - expiry: now.getTime() + expiryDay, - } - localStorage.setItem(key, JSON.stringify(item)) - }, - - get: function getWithExpiry(key) { - const itemStr = localStorage.getItem(key) - - if (!itemStr) { - return undefined - } - const item = JSON.parse(itemStr) - const now = new Date() - - if (now.getTime() > item.expiry) { - localStorage.removeItem(key) - return undefined - } - return item.value - } - } - - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config_site.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config_site.pug deleted file mode 100755 index 19eb7321d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/config_site.pug +++ /dev/null @@ -1,19 +0,0 @@ -- - let isHighlightShrink - if (theme.highlight_shrink == 'none') isHighlightShrink = 'undefined' - else if (page.highlight_shrink === true || page.highlight_shrink === false) isHighlightShrink = page.highlight_shrink - else isHighlightShrink = theme.highlight_shrink - - const pageToc = page.toc === true || page.toc === false ? page.toc : theme.toc.enable - const showToc = is_post() && theme.aside.enable && pageToc && (toc(page.content) !== '' || page.encrypt == true ) -- - -script#config_change - . - var GLOBAL_CONFIG_SITE = { - isPost: !{is_post()}, - isHome: !{is_home()}, - isHighlightShrink: !{isHighlightShrink}, - isToc: !{showToc}, - postUpdate: '!{full_date(page.updated)}' - } diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/google_adsense.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/google_adsense.pug deleted file mode 100755 index 3ef1af994..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/google_adsense.pug +++ /dev/null @@ -1,9 +0,0 @@ -if (theme.google_adsense && theme.google_adsense.enable) - script(async src=theme.google_adsense.js) - - if theme.google_adsense.auto_ads - script. - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: '!{theme.google_adsense.client}', - enable_page_level_ads: '!{theme.google_adsense.enable_page_level_ads}' - }); \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/js.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/js.pug deleted file mode 100755 index 058b80ac4..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/js.pug +++ /dev/null @@ -1,65 +0,0 @@ -script - | (function () { - - if theme.darkmode.enable - | window.activateDarkMode = function () { - | document.documentElement.setAttribute('data-theme', 'dark') - | if (document.querySelector('meta[name="theme-color"]') !== null) { - | document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d') - | } - | } - | window.activateLightMode = function () { - | document.documentElement.setAttribute('data-theme', 'light') - | if (document.querySelector('meta[name="theme-color"]') !== null) { - | document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff') - | } - | } - - | const autoChangeMode = '#{theme.darkmode.autoChangeMode}' - | const t = saveToLocal.get('theme') - | if (autoChangeMode === '1') { - | const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches - | const isLightMode = window.matchMedia('(prefers-color-scheme: light)').matches - | const isNotSpecified = window.matchMedia('(prefers-color-scheme: no-preference)').matches - | const hasNoSupport = !isDarkMode && !isLightMode && !isNotSpecified - - | if (t === undefined) { - | if (isLightMode) activateLightMode() - | else if (isDarkMode) activateDarkMode() - | else if (isNotSpecified || hasNoSupport) { - | const now = new Date() - | const hour = now.getHours() - | const isNight = hour <= 6 || hour >= 18 - | isNight ? activateDarkMode() : activateLightMode() - | } - | window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) { - | if (saveToLocal.get('theme') === undefined) { - | e.matches ? activateDarkMode() : activateLightMode() - | } - | }) - | } else if (t === 'light') activateLightMode() - | else activateDarkMode() - | } else if (autoChangeMode === '2') { - | const now = new Date() - | const hour = now.getHours() - | const isNight = hour <= 6 || hour >= 18 - | if (t === undefined) isNight ? activateDarkMode() : activateLightMode() - | else if (t === 'light') activateLightMode() - | else activateDarkMode() - | } else { - | if (t === 'dark') activateDarkMode() - | else if (t === 'light') activateLightMode() - | } - - if theme.aside.enable && theme.aside.button - | const asideStatus = saveToLocal.get('aside-status') - | if (asideStatus !== undefined) { - | if (asideStatus === 'hide') { - | document.documentElement.classList.add('hide-aside') - | } else { - | document.documentElement.classList.remove('hide-aside') - | } - | } - - | })() - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/noscript.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/noscript.pug deleted file mode 100755 index cc3befa69..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/noscript.pug +++ /dev/null @@ -1,14 +0,0 @@ -noscript. - \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/preconnect.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/preconnect.pug deleted file mode 100755 index 3d7b9e994..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/preconnect.pug +++ /dev/null @@ -1,22 +0,0 @@ -link(rel="preconnect" href="//cdn.jsdelivr.net") - -if theme.google_analytics - link(rel="preconnect" href="//www.google-analytics.com" crossorigin) - -if theme.baidu_analytics - link(rel="preconnect" href="//hm.baidu.com") - -if theme.tencent_analytics - link(rel="preconnect" href="//pingjs.qq.com") - -if theme.cnzz_analytics - link(rel="preconnect" href="//s4.cnzz.com") - -if theme.blog_title_font && theme.blog_title_font.font_link && theme.blog_title_font.font_link.indexOf('//fonts.googleapis.com') != -1 - link(rel="preconnect" href="//fonts.googleapis.com" crossorigin) - -if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv - link(rel="preconnect" href="//busuanzi.ibruce.info") - -if theme.baidu_push - link(rel="preconnect" href="//zz.bdstatic.com") diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/pwa.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/pwa.pug deleted file mode 100755 index e9152f2e8..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/pwa.pug +++ /dev/null @@ -1,11 +0,0 @@ -link(rel="manifest" href=url_for(theme.pwa.manifest)) -if(theme.pwa.theme_color) - meta(name="msapplication-TileColor" content=theme.pwa.theme_color) -if(theme.pwa.apple_touch_icon) - link(rel="apple-touch-icon" sizes="180x180" href=url_for(theme.pwa.apple_touch_icon)) -if(theme.pwa.favicon_32_32) - link(rel="icon" type="image/png" sizes="32x32" href=url_for(theme.pwa.favicon_32_32)) -if(theme.pwa.favicon_16_16) - link(rel="icon" type="image/png" sizes="16x16" href=url_for(theme.pwa.favicon_16_16)) -if(theme.pwa.mask_icon) - link(rel="mask-icon" href=url_for(theme.pwa.mask_icon) color="#5bbad5") diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/site_verification.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/site_verification.pug deleted file mode 100755 index cae574289..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/head/site_verification.pug +++ /dev/null @@ -1,14 +0,0 @@ -if theme.google_site_verification - meta(name="google-site-verification" content=theme.google_site_verification) - -if theme.bing_site_verification - meta(name="msvalidate.01" content=theme.bing_site_verification) - -if theme.baidu_site_verification - meta(name="baidu-site-verification" content=theme.baidu_site_verification) - -if theme.qihu_site_verification - meta(name="360-site-verification" content=theme.qihu_site_verification) - -if theme.yandex_site_verification - meta(name="yandex-verification" content=theme.yandex_site_verification) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/index.pug deleted file mode 100755 index 4fe2e2ccb..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/index.pug +++ /dev/null @@ -1,48 +0,0 @@ -if page.top_img !== false - if is_post() - - var top_img = page.top_img || page.cover || page.randomcover || theme.default_top_img - else if is_page() - - var top_img = page.top_img || theme.default_top_img - else if is_tag() - - var top_img = theme.tag_per_img && theme.tag_per_img[page.tag] - - top_img = top_img ? top_img : (theme.tag_img !== false ? theme.tag_img || theme.default_top_img : false) - else if is_category() - - var top_img = theme.category_per_img && theme.category_per_img[page.category] - - top_img = top_img ? top_img : (theme.category_img !== false ? theme.category_img || theme.default_top_img : false) - else if is_home() - - var top_img = theme.index_img !== false ? theme.index_img || theme.default_top_img : false - else if is_archive() - - var top_img = theme.archive_img !== false ? theme.archive_img || theme.default_top_img : false - else - - var top_img = page.top_img || theme.default_top_img - - if top_img !== false - - var imgSource = top_img && top_img.indexOf('/') !== -1 ? `background-image: url(${url_for(top_img)})` : `background: ${top_img}` - - var bg_img = top_img ? imgSource : '' - - var site_title = is_archive() ? _p('page.archives') : page.title || page.tag || page.category || config.title - - var isHomeClass = is_home() ? 'full_page' : 'not-index-bg' - - is_post() ? isHomeClass = 'post-bg' : isHomeClass - else - - var isHomeClass = 'no-top-img' -else - - var top_img = false - - var isHomeClass = 'no-top-img' - -header#page-header(class=isHomeClass style=bg_img) - !=partial('includes/header/nav', {}, {cache:theme.fragment_cache}) - if top_img !== false - if is_post() - include ./post-info.pug - else if is_home() - #site-info - h1#site-title=site_title - #site-subtitle - span#subtitle - if(theme.social) - #site_social_icons - !=fragment_cache('social', function(){return partial('includes/header/social')}) - #scroll-down - i.fas.fa-angle-down.scroll-down-effects - else - #page-site-info - h1#site-title=site_title diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/menu_item.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/menu_item.pug deleted file mode 100755 index 7feff6b01..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/menu_item.pug +++ /dev/null @@ -1,26 +0,0 @@ -if theme.menu - //- for mobile sidebar - - let sidebarChildHide = theme.hide_sidebar_menu_child ? 'hide' : '' - - .menus_items - each value, label in theme.menu - if !Array.isArray(value) - .menus_item - a.site-page(href=url_for(trim(value.split('||')[0]))) - if value.split('||')[1] - i.fa-fw(class=trim(value.split('||')[1])) - span=' '+label - else - .menus_item - a.site-page(href='javascript:void(0);') - if label.split('||')[1] - i.fa-fw(class=trim(label.split('||')[1])) - span=' '+ trim(label.split('||')[0]) - i.fas.fa-chevron-down.expand(class=sidebarChildHide) - ul.menus_item_child - each i in value - li - a.site-page(href=url_for(trim(i.split('||')[1]))) - if i.split('||')[2] - i.fa-fw(class=trim(i.split('||')[2])) - span=' '+trim(i.split('||')[0]) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/nav.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/nav.pug deleted file mode 100755 index 332adf64b..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/nav.pug +++ /dev/null @@ -1,17 +0,0 @@ -nav#nav - span#blog_name - a#site-name(href=url_for('/')) #[=config.title] - - span#menus - if (theme.algolia_search.enable || theme.local_search.enable) - #search_button - a.site-page.social-icon.search - i.fas.fa-search.fa-fw - span=' '+_p('search') - !=fragment_cache('menus', function(){return partial('includes/header/menu_item')}) - - span#toggle-menu.close - a.site-page - i.fas.fa-bars.fa-fw - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/post-info.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/post-info.pug deleted file mode 100755 index 2d9492855..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/post-info.pug +++ /dev/null @@ -1,82 +0,0 @@ -#post-info - h1.post-title= page.title || _p('no_title') - - #post-meta - .meta-firstline - if (theme.post_meta.post.date_type) - span.post-meta-date - if (theme.post_meta.post.date_type === 'both') - i.far.fa-calendar-alt.fa-fw.post-meta-icon - span.post-meta-label= _p('post.created') - time.post-meta-date-created(datetime=date_xml(page.date) title=_p('post.created') + ' ' + full_date(page.date))=date(page.date, config.date_format) - span.post-meta-separator | - i.fas.fa-history.fa-fw.post-meta-icon - span.post-meta-label= _p('post.updated') - time.post-meta-date-updated(datetime=date_xml(page.updated) title=_p('post.updated') + ' ' + full_date(page.updated))=date(page.updated, config.date_format) - else - - let data_type_update = theme.post_meta.post.date_type === 'updated' - - let date_type = data_type_update ? 'updated' : 'date' - - let date_icon = data_type_update ? 'fas fa-history' :'far fa-calendar-alt' - - let data_info = data_type_update ? _p('post.updated') : _p('post.created') - i.fa-fw.post-meta-icon(class=date_icon) - span.post-meta-label= data_info - time(datetime=date_xml(page[date_type]) title=date_title + ' ' + full_date(page[date_type]))=date(page[date_type], config.date_format) - if (theme.post_meta.post.categories && page.categories.data.length > 0) - span.post-meta-categories - if (theme.post_meta.post.date_type) - span.post-meta-separator | - - each item, index in page.categories.data - i.fas.fa-inbox.fa-fw.post-meta-icon - a(href=url_for(item.path)).post-meta-categories #[=item.name] - if (index < page.categories.data.length - 1) - i.fas.fa-angle-right.post-meta-separator - - .meta-secondline - - let postWordcount = theme.wordcount.enable && (theme.wordcount.post_wordcount || theme.wordcount.min2read) - if (postWordcount) - span.post-meta-separator | - span.post-meta-wordcount - if theme.wordcount.post_wordcount - i.far.fa-file-word.fa-fw.post-meta-icon - span.post-meta-label= _p('post.wordcount') + ':' - span.word-count= wordcount(page.content) - if theme.wordcount.min2read - span.post-meta-separator | - if theme.wordcount.min2read - i.far.fa-clock.fa-fw.post-meta-icon - span.post-meta-label= _p('post.min2read') + ':' - span= min2read(page.content, {cn: 350, en: 160}) + _p('post.min2read_unit') - - if theme.busuanzi.page_pv - span.post-meta-separator | - span.post-meta-pv-cv - i.far.fa-eye.fa-fw.post-meta-icon - span.post-meta-label=_p('post.page_pv') + ':' - span#busuanzi_value_page_pv - - if !theme.comments.lazyload && page.comments !== false && theme.comments.use && theme.comments.count - - var whichCount = theme.comments.use[0] - if whichCount !== 'Livere' && whichCount !== 'Utterances' - span.post-meta-separator | - span.post-meta-commentcount - if whichCount === 'Disqus' || whichCount === 'Disqusjs' - i.far.fa-comments.fa-fw.post-meta-icon - span.post-meta-label= _p('post.comments') + ':' - span.disqus-comment-count.comment-count - a(href=full_url_for(page.path) + '#disqus_thread') - else if whichCount === 'Valine' - i.far.fa-comments.fa-fw.post-meta-icon - span.post-meta-label= _p('post.comments') + ':' - a(href=url_for(page.path) + '#post-comment' itemprop="discussionUrl") - span.valine-comment-count.comment-count(data-xid=url_for(page.path) itemprop="commentCount") - else if whichCount === 'Gitalk' - i.far.fa-comments.fa-fw.post-meta-icon - span.post-meta-label= _p('post.comments') + ':' - a(href=url_for(page.path) + '#post-comment') - span.gitalk-comment-count.comment-count - else if whichCount === 'Facebook Comments' - i.far.fa-comments.fa-fw.post-meta-icon - span.post-meta-label= _p('post.comments') + ':' - a.comment-count(href=url_for(page.path) + '#post-comment') - span.fb-comments-count(data-href=urlNoIndex()) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/social.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/social.pug deleted file mode 100755 index 80adfe94f..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/header/social.pug +++ /dev/null @@ -1,4 +0,0 @@ -each url, icon in theme.social - a.social-icon(href=url_for(trim(url.split('||')[0])) target="_blank" - title=url.split('||')[1] === undefined ? '' : trim(url.split('||')[1])) - i(class=icon) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/layout.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/layout.pug deleted file mode 100755 index 05d6c2979..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/layout.pug +++ /dev/null @@ -1,47 +0,0 @@ -- var htmlClassHideAside = theme.aside.enable && theme.aside.hide ? 'hide-aside' : '' -- var hideAside = !theme.aside.enable || page.aside === false ? 'hide-aside' : '' - -doctype html -html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside) - head - include ./head.pug - body - - - - - - if theme.preloader - !=partial('includes/loading/loading', {}, {cache:theme.fragment_cache}) - - if theme.background - #web_bg - - !=partial('includes/sidebar', {}, {cache:theme.fragment_cache}) - - #body-wrap - include ./header/index.pug - - main#content-inner.layout(class=hideAside) - if body - div!= body - else - block content - if theme.aside.enable && page.aside !== false - include widget/index.pug - - - var footerBg = theme.footer_bg - if (footerBg) - if (footerBg === true) - - var footer_bg = bg_img - else - - var footer_bg = theme.footer_bg.indexOf('/') !== -1 ? `background-image: url(${url_for(footerBg)})` : `background: ${footerBg}` - else - - var footer_bg = '' - - footer#footer(style=footer_bg) - !=partial('includes/footer', {}, {cache:theme.fragment_cache}) - - include ./rightside.pug - !=partial('includes/third-party/search/index', {}, {cache:theme.fragment_cache}) - include ./additional-js.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading-js.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading-js.pug deleted file mode 100755 index 6bf7f2de5..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading-js.pug +++ /dev/null @@ -1,13 +0,0 @@ -script. - var preloader = { - endLoading: () => { - document.body.style.overflow = 'auto'; - document.getElementById('loading-box').classList.add("loaded") - }, - initLoading: () => { - document.body.style.overflow = ''; - document.getElementById('loading-box').classList.remove("loaded") - - } - } - window.addEventListener('load',()=> {preloader.endLoading()}) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading.pug deleted file mode 100755 index 2e4b65f5d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/loading/loading.pug +++ /dev/null @@ -1,9 +0,0 @@ -#loading-box - .loading-left-bg - .loading-right-bg - .spinner-box - .configure-border-1 - .configure-core - .configure-border-2 - .configure-core - .loading-word= _p('loading') diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/article-sort.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/article-sort.pug deleted file mode 100755 index 6ac95b89f..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/article-sort.pug +++ /dev/null @@ -1,23 +0,0 @@ -mixin articleSort(posts) - .article-sort - - var year - - posts.each(function (article) { - - let tempYear = date(article.date, 'YYYY') - - let no_cover = article.cover === false || !theme.cover.archives_enable ? 'no-article-cover' : '' - - let title = article.title || _p('no_title') - if tempYear !== year - - year = tempYear - .article-sort-item.year= year - .article-sort-item(class=no_cover) - if article.cover && theme.cover.archives_enable - a.article-sort-item-img(href=url_for(article.path) title=title) - if theme.lazyload.enable - img(data-lazy-src=url_for(article.cover) alt=title onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'`) - else - img(src=url_for(article.cover) alt=title onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'`) - .article-sort-item-info - a.article-sort-item-title(href=url_for(article.path) title=title)= title - .article-sort-item-time - i.far.fa-calendar-alt - time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))= date(article.date, config.date_format) - - }) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/post-ui.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/post-ui.pug deleted file mode 100755 index 7748b8ea5..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/mixins/post-ui.pug +++ /dev/null @@ -1,81 +0,0 @@ -mixin postUI(posts) - each article , index in page.posts.data - .recent-post-item - - let link = article.link || article.path - - let title = article.title || _p('no_title') - - let leftOrRight = index%2 == 0 ? 'left_radius' : 'right_radius' - - let post_cover = article.cover - - let no_cover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : '' - if post_cover && theme.cover.index_enable - .post_cover(class=leftOrRight) - a(href=url_for(link) title=title) - if theme.lazyload.enable - img.post_bg(data-lazy-src=url_for(post_cover) onerror=`this.onerror=null;this.src='`+ url_for(theme.error_img.post_page) + `'` alt=title) - else - img.post_bg(src=url_for(post_cover) onerror=`this.onerror=null;this.src='`+ url_for(theme.error_img.post_page) + `'` alt=title) - .recent-post-info(class=no_cover) - a.article-title(href=url_for(link) title=title)= title - .article-meta-wrap - if (is_home() && (article.top || article.sticky > 0)) - span.article-meta - i.fas.fa-thumbtack.article-meta__icon.sticky - span.sticky= _p('sticky') - span.article-meta__separator | - if (theme.post_meta.page.date_type) - span.post-meta-date - if (theme.post_meta.page.date_type === 'both') - i.far.fa-calendar-alt - span.article-meta-label=_p('post.created') - time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))=date(article.date, config.date_format) - span.article-meta__separator | - i.fas.fa-history - span.article-meta-label=_p('post.updated') - time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))=date(article.updated, config.date_format) - else - - let data_type_updated = theme.post_meta.page.date_type === 'updated' - - let date_type = data_type_updated ? 'updated' : 'date' - - let date_icon = data_type_updated ? 'fas fa-history' :'far fa-calendar-alt' - - let date_title = data_type_updated ? _p('post.updated') : _p('post.created') - i(class=date_icon) - span.article-meta-label=date_title - time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format) - if (theme.post_meta.page.categories && article.categories.data.length > 0) - span.article-meta - span.article-meta__separator | - i.fas.fa-inbox.article-meta__icon - each item, index in article.categories.data - a(href=url_for(item.path)).article-meta__categories #[=item.name] - if (index < article.categories.data.length - 1) - i.fas.fa-angle-right - if (theme.post_meta.page.tags && article.tags.data.length > 0) - span.article-meta.tags - span.article-meta__separator | - i.fas.fa-tag.article-meta__icon - each item, index in article.tags.data - a(href=url_for(item.path)).article-meta__tags #[=item.name] - if (index < article.tags.data.length - 1) - span.article-meta__link #[='•'] - - //- Display the article introduction on homepage - case theme.index_post_content.method - when false - - break - when 1 - .content!= article.description - when 2 - if article.description - .content!= article.description - else - - const content = strip_html(article.content) - - let expert = content.substring(0, theme.index_post_content.length) - - content.length > theme.index_post_content.length ? expert += ' ...' : '' - .content!= expert - default - - const content = strip_html(article.content) - - let expert = content.substring(0, theme.index_post_content.length) - - content.length > theme.index_post_content.length ? expert += ' ...' : '' - .content!= expert - - if theme.ad && theme.ad.index - if (index + 1) % 3 == 0 - .recent-post-item.ad-height!=theme.ad.index diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/artitalk.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/artitalk.pug deleted file mode 100755 index c5128bbc0..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/artitalk.pug +++ /dev/null @@ -1,4 +0,0 @@ -if top_img === false - h1.page-title= page.title - -#artitalk_main \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/categories.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/categories.pug deleted file mode 100755 index dbdc98063..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/categories.pug +++ /dev/null @@ -1,5 +0,0 @@ -.category-lists - .category-title.is-center= _p('page.category') - | - - span.category-amount= site.categories.length - div!= list_categories() \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/default-page.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/default-page.pug deleted file mode 100755 index 2d9cdda6e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/default-page.pug +++ /dev/null @@ -1,4 +0,0 @@ -#article-container - if top_img === false - h1.page-title= page.title - != page.content \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/flink.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/flink.pug deleted file mode 100755 index 48bcf3207..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/flink.pug +++ /dev/null @@ -1,19 +0,0 @@ -.flink#article-container - if site.data.link - each i in site.data.link - if i.class_name - h2!= i.class_name - if i.class_desc - .flink-desc!=i.class_desc - .flink-list - each item in i.link_list - .flink-list-item - a(href=url_for(item.link) title=item.name target="_blank") - if theme.lazyload.enable - img(data-lazy-src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name ) - else - img(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name ) - span.flink-item-name= item.name - span.flink-item-desc(title=item.descr)= item.descr - != page.content - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/tags.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/tags.pug deleted file mode 100755 index 4d57e4374..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/page/tags.pug +++ /dev/null @@ -1,5 +0,0 @@ -.tag-cloud-title.is-center= _p('page.tag') - | - - span.tag-cloud-amount= site.tags.length -.tag-cloud-list.is-center - !=cloudTags({source: site.tags, minfontsize: 1.2, maxfontsize: 2.1, limit: 0, unit: 'em'}) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/pagination.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/pagination.pug deleted file mode 100755 index ab09c1aba..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/pagination.pug +++ /dev/null @@ -1,39 +0,0 @@ -- - var options = { - prev_text: '', - next_text: '', - mid_size: 1, - escape: false - } - -if(!is_post()) - nav#pagination - div.pagination - !=paginator(options) -else - nav#pagination.pagination-post - if(page.prev) - - var hasPageNext = page.next ? 'pull-left' : 'pull-full' - .prev-post(class=hasPageNext) - - var pagination_cover = page.prev.cover === false ? page.prev.randomcover : page.prev.cover - a(href=url_for(page.prev.path)) - if theme.lazyload.enable - img.prev-cover(data-lazy-src=url_for(pagination_cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` ) - else - img.prev-cover(src=url_for(pagination_cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` ) - .pagination-info - .label=_p('pagination.prev') - .prev_info=page.prev.title - - if(page.next) - - var hasPagePrev = page.prev ? 'pull-right' : 'pull-full' - - var pagination_cover = page.next.cover == false ? page.next.randomcover : page.next.cover - .next-post(class=hasPagePrev) - a(href=url_for(page.next.path)) - if theme.lazyload.enable - img.next-cover(data-lazy-src=url_for(pagination_cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'`) - else - img.next-cover(src=url_for(pagination_cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'`) - .pagination-info - .label=_p('pagination.next') - .next_info=page.next.title \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/post-copyright.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/post-copyright.pug deleted file mode 100755 index dfa346daa..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/post-copyright.pug +++ /dev/null @@ -1,17 +0,0 @@ -if theme.post_copyright.enable && page.copyright !== false - - let author = page.copyright_author ? page.copyright_author : config.author - - let authorHref = page.copyright_author_href ? page.copyright_author_href : `mailto:${config.email}` - - let url = page.copyright_url ? page.copyright_url : page.permalink - - let info = page.copyright_info ? page.copyright_info : _p('post.copyright.copyright_content', theme.post_copyright.license_url, theme.post_copyright.license, config.url, config.title) - .post-copyright - .post-copyright__author - span.post-copyright-meta= _p('post.copyright.author') + ": " - span.post-copyright-info - a(href=authorHref)=author - .post-copyright__type - span.post-copyright-meta= _p('post.copyright.link') + ": " - span.post-copyright-info - a(href=url_for(url))= theme.post_copyright.decode ? decodeURI(url) : url - .post-copyright__notice - span.post-copyright-meta= _p('post.copyright.copyright_notice') + ": " - span.post-copyright-info!= info diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/reward.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/reward.pug deleted file mode 100755 index 1bf93816b..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/post/reward.pug +++ /dev/null @@ -1,16 +0,0 @@ -.post-reward - .reward-button - i.fas.fa-qrcode - = ' ' + _p('donate') - .reward-main - ul.reward-all - each item in theme.reward.QR_code - - var clickTo = (item.itemlist||item).link ? (item.itemlist||item).link : (item.itemlist||item).img - li.reward-item - a(href=clickTo target='_blank') - if theme.lazyload.enable - img.post-qr-code-img(data-lazy-src=url_for((item.itemlist||item).img) alt=(item.itemlist||item).text) - else - img.post-qr-code-img(src=url_for((item.itemlist||item).img) alt=(item.itemlist||item).text) - .post-qr-code-desc=(item.itemlist||item).text - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/rightside.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/rightside.pug deleted file mode 100755 index 9a9e491da..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/rightside.pug +++ /dev/null @@ -1,33 +0,0 @@ -#rightside - #rightside-config-hide - if is_post() && theme.readmode - button#readmode(type="button" title=_p('rightside.readmode_title')) - i.fas.fa-book-open - if theme.translate.enable - button#translateLink(type="button" title=_p('rightside.translate_title'))= theme.translate.default - if theme.darkmode.enable && theme.darkmode.button - button#darkmode(type="button" title=_p('rightside.night_mode_title')) - i.fas.fa-adjust - if theme.aside.enable && theme.aside.button && page.aside !== false - button#hide-aside-btn(type="button") - i.fas.fa-arrows-alt-h - #rightside-config-show - if is_post() - if (theme.readmode || theme.translate.enable || (theme.darkmode.enable && theme.darkmode.button)) - button#rightside_config(type="button" title=_p("rightside.setting")) - i.fas.fa-cog - if showToc && theme.aside.mobile - button#mobile-toc-button.close(type="button" title=_p("rightside.toc")) - i.fas.fa-list-ul - else if theme.translate.enable || (theme.darkmode.enable && theme.darkmode.button) - button#rightside_config(type="button" title=_p("rightside.setting")) - i.fas.fa-cog - - if theme.chat_btn - button#chat_btn(type="button" title=_p("rightside.chat_btn")) - i.fas.fa-sms - if commentsJsLoad - a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment")) - i.fas.fa-comments - button#go-up(type="button" title=_p("rightside.back_to_top")) - i.fas.fa-arrow-up \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/sidebar.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/sidebar.pug deleted file mode 100755 index 43c0b2c57..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/sidebar.pug +++ /dev/null @@ -1,31 +0,0 @@ -#sidebar - #menu-mask - #sidebar-menus - .author-avatar - if theme.lazyload.enable - img.avatar-img(data-lazy-src=url_for(theme.avatar.img) onerror=`onerror=null;src='${theme.error_img.flink}'` alt="avatar") - else - img.avatar-img(src=url_for(theme.avatar.img) onerror=`onerror=null;src='${theme.error_img.flink}'` alt="avatar") - .site-data - if site.posts.length - .data-item.is-center - .data-item-link - a(href=url_for(config.archive_dir) + '/') - .headline= _p('aside.articles') - .length-num= site.posts.length - - if site.tags.length - .data-item.is-center - .data-item-link - a(href=url_for(config.tag_dir) + '/' ) - .headline= _p('aside.tags') - .length-num= site.tags.length - - if site.categories.length - .data-item.is-center - .data-item-link - a(href=url_for(config.category_dir) + '/') - .headline= _p('aside.categories') - .length-num= site.categories.length - hr - !=fragment_cache('menus', function(){return partial('includes/header/menu_item')}) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/aplayer.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/aplayer.pug deleted file mode 100755 index c7aa01099..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/aplayer.pug +++ /dev/null @@ -1,3 +0,0 @@ -link(rel='stylesheet' href=url_for(theme.CDN.aplayer_css)) -script(src=url_for(theme.CDN.aplayer_js)) -script(src=url_for(theme.CDN.meting_js)) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/artitalk.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/artitalk.pug deleted file mode 100755 index 40490fb8d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/artitalk.pug +++ /dev/null @@ -1,24 +0,0 @@ -- let option = theme.artitalk.option ? JSON.stringify(theme.artitalk.option) : false - -script. - (()=>{ - let setting = { - appId: '!{theme.artitalk.appId}', - appKey: '!{theme.artitalk.appKey}', - } - - if (!{Boolean(option)}) { - const otherSetting = !{option} - setting = Object.assign({}, setting, otherSetting) - } - - const init = () => { - new Artitalk(setting) - } - - if (typeof Artitalk === 'function') { - init() - } else { - $.getScript('!{theme.CDN.artitalk}',init) - } - })() \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/baidu_push.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/baidu_push.pug deleted file mode 100755 index 13faf11e2..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/baidu_push.pug +++ /dev/null @@ -1,15 +0,0 @@ -if theme.baidu_push - script. - (function(){ - const bp = document.createElement('script'); - const curProtocol = window.location.protocol.split(':')[0]; - if (curProtocol === 'https'){ - bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; - } - else{ - bp.src = 'http://push.zhanzhang.baidu.com/push.js'; - } - bp.dataset.pjax = '' - const s = document.getElementsByTagName("script")[0]; - s.parentNode.insertBefore(bp, s); - })() \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/chatra.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/chatra.pug deleted file mode 100755 index b6c120146..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/chatra.pug +++ /dev/null @@ -1,33 +0,0 @@ -//- https://chatra.io/help/api/ -script. - (function(d, w, c) { - w.ChatraID = '#{theme.chatra.id}'; - var s = d.createElement('script'); - w[c] = w[c] || function() { - (w[c].q = w[c].q || []).push(arguments); - }; - s.async = true; - s.src = 'https://call.chatra.io/chatra.js'; - if (d.head) d.head.appendChild(s); - })(document, window, 'Chatra'); - - if (!{theme.chat_btn}) { - var chatBtnFn = () => { - var chatBtn = document.getElementById("chat_btn") - chatBtn.addEventListener("click", function(){ - Chatra('openChat') - }); - } - chatBtnFn() - } else { - if (!{theme.chat_hide_show}) { - function chatBtnHide () { - Chatra('hide') - } - function chatBtnShow () { - Chatra('show') - } - } - } - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/crisp.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/crisp.pug deleted file mode 100755 index cc57e8dd2..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/crisp.pug +++ /dev/null @@ -1,36 +0,0 @@ -script. - window.$crisp = []; - window.CRISP_WEBSITE_ID = "!{theme.crisp.website_id}"; - (function () { - d = document; - s = d.createElement("script"); - s.src = "https://client.crisp.chat/l.js"; - s.async = 1; - d.getElementsByTagName("head")[0].appendChild(s); - })(); - $crisp.push(["safe", true]) - - if (!{theme.chat_btn}) { - $crisp.push(["do", "chat:hide"]) - $crisp.push(["on", "chat:closed", function() { - $crisp.push(["do", "chat:hide"]) - }]) - var chatBtnFn = () => { - var chatBtn = document.getElementById("chat_btn") - chatBtn.addEventListener("click", function(){ - $crisp.push(["do", "chat:show"]) - $crisp.push(["do", "chat:open"]) - - }); - } - chatBtnFn() - } else { - if (!{theme.chat_hide_show}) { - function chatBtnHide () { - $crisp.push(["do", "chat:hide"]) - } - function chatBtnShow () { - $crisp.push(["do", "chat:show"]) - } - } - } \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/daovoice.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/daovoice.pug deleted file mode 100755 index 3c6b06493..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/daovoice.pug +++ /dev/null @@ -1,40 +0,0 @@ -//- https://guide.daocloud.io/daovoice/javascript-api-5869833.html -script. - (function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/!{theme.daovoice.app_id}.js","daovoice") - -script. - var isChatBtn = !{theme.chat_btn} - daovoice('init', { - app_id: '!{theme.daovoice.app_id}',},{ - launcher: { - disableLauncherIcon: isChatBtn // 悬浮 ICON 是否显示 - }, - }); - daovoice('update'); - - if (isChatBtn) { - var chatBtnFn = () => { - var chatBtn = document.getElementById("chat_btn") - chatBtn.addEventListener("click", function(){ - daovoice('show') - }); - } - chatBtnFn() - } else { - if (!{theme.chat_hide_show}) { - function chatBtnHide () { - daovoice('update', {},{ - launcher: { - disableLauncherIcon: true // 悬浮 ICON 是否显示 - }, - }); - } - function chatBtnShow () { - daovoice('update', {},{ - launcher: { - disableLauncherIcon: false // 悬浮 ICON 是否显示 - }, - }); - } - } - } \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/gitter.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/gitter.pug deleted file mode 100755 index 46f979a9a..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/gitter.pug +++ /dev/null @@ -1,43 +0,0 @@ -if theme.chat_btn - script. - ((window.gitter = {}).chat = {}).options = { - disableDefaultChat: true, - }; - document.addEventListener('gitter-sidecar-ready', (e) => { - const GitterChat = e.detail.Chat - let chat - - function initGitter () { - chat = new GitterChat({ - room: '#{theme.gitter.room}', - activationElement: '#chat_btn' - }); - } - - initGitter() - - if (!{theme.pjax.enable}) { - document.addEventListener('pjax:complete', () => { - chat.destroy() - initGitter() - }) - } - - }) -else - script. - ((window.gitter = {}).chat = {}).options = { - room: '#{theme.gitter.room}', - }; - - if (!{theme.chat_hide_show}) { - function chatBtnHide () { - document.getElementsByClassName('gitter-open-chat-button')[0].style.display= 'none' - } - - function chatBtnShow () { - document.getElementsByClassName('gitter-open-chat-button')[0].style.display= 'block' - } - } - -script(src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/index.pug deleted file mode 100755 index db78a34c8..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/index.pug +++ /dev/null @@ -1,10 +0,0 @@ -if theme.chatra && theme.chatra.enable - include ./chatra.pug -else if theme.tidio && theme.tidio.enable - include ./tidio.pug -else if theme.daovoice && theme.daovoice.enable - include ./daovoice.pug -else if theme.gitter && theme.gitter.enable - include ./gitter.pug -else if theme.crisp && theme.crisp.enable - include ./crisp.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/tidio.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/tidio.pug deleted file mode 100755 index 6b0097fb6..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/chat/tidio.pug +++ /dev/null @@ -1,41 +0,0 @@ -script(src=`//code.tidio.co/${theme.tidio.public_key}.js` async) - -if theme.chat_btn - script. - function onTidioChatApiReady() { - window.tidioChatApi.hide(); - window.tidioChatApi.on("close", function() { - window.tidioChatApi.hide(); - }); - } - if (window.tidioChatApi) { - window.tidioChatApi.on("ready", onTidioChatApiReady); - } else { - document.addEventListener("tidioChat-ready", onTidioChatApiReady); - } - - var chatBtnFn = () => { - document.getElementById("chat_btn").addEventListener("click", function(){ - window.tidioChatApi.show(); - window.tidioChatApi.open(); - }); - } - chatBtnFn() - -else if theme.chat_hide_show - script. - function chatBtnHide () { - if (window.tidioChatApi) { - //- window.tidioChatApi.hide(); - document.getElementById('tidio-chat').style.display= 'none' - } - } - - function chatBtnShow () { - if (window.tidioChatApi) { - //- window.tidioChatApi.show(); - document.getElementById('tidio-chat').style.display= 'block' - } - } - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqus.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqus.pug deleted file mode 100755 index fd44d2a8d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqus.pug +++ /dev/null @@ -1,45 +0,0 @@ -script. - function loadDisqus () { - var disqus_config = function () { - this.page.url = '!{ page.permalink }' - this.page.identifier = '!{ page.path }' - this.page.title = '!{ page.title }' - }; - - window.disqusReset = () => { - DISQUS.reset({ - reload: true, - config: disqus_config - }) - } - - if (window.DISQUS) disqusReset() - else { - (function() { - var d = document, s = d.createElement('script'); - s.src = 'https://!{theme.disqus.shortname}.disqus.com/embed.js'; - s.setAttribute('data-timestamp', +new Date()); - (d.head || d.body).appendChild(s); - })(); - } - } - - if ('!{theme.comments.use[0]}' === 'Disqus' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus) - else loadDisqus() - } else { - function loadOtherComment () { - loadDisqus() - } - } - -if is_post() && !theme.comments.lazyload && theme.comments.count && theme.comments.use[0] === 'Disqus' - script. - if (window.DISQUSWIDGETS === undefined) { - var d = document, s = d.createElement('script'); - s.src = 'https://!{theme.disqus.shortname}.disqus.com/count.js'; - s.id = 'dsq-count-scr'; - (d.head || d.body).appendChild(s); - } else { - DISQUSWIDGETS.getCount({reset: true}); - } diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqusjs.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqusjs.pug deleted file mode 100755 index e2f313646..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/disqusjs.pug +++ /dev/null @@ -1,58 +0,0 @@ -- let disqusjsApi = theme.disqusjs.api || 'https://disqus.skk.moe/disqus/' -script. - function loadDisqusjs () { - function addDisqusjsCSS () { - const ele = document.createElement('link') - ele.rel = 'stylesheet' - ele.href= '!{url_for(theme.CDN.disqusjs_css)}' - document.getElementsByTagName('head')[0].appendChild(ele) - } - - function initDisqusjs () { - window.DISQUS = null - new DisqusJS({ - shortname: '!{theme.disqusjs.shortname}', - siteName: '!{theme.disqusjs.siteName}', - identifier: '!{ page.path }', - url: '!{ page.permalink }', - title: '!{ page.title }', - api: '!{disqusjsApi}', - apikey: '!{theme.disqusjs.apikey}', - nocomment: '!{theme.disqusjs.nocomment}', - admin: '!{theme.disqusjs.admin}', - adminLabel: '!{theme.disqusjs.adminLabel}' - }); - } - - window.disqusReset = initDisqusjs - - if (window.disqusJsLoad) initDisqusjs() - else { - addDisqusjsCSS() - $.getScript('!{url_for(theme.CDN.disqusjs)}', initDisqusjs) - window.disqusJsLoad = true - } - } - - if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqusjs) - else loadDisqusjs() - } - else { - function loadOtherComment () { - loadDisqusjs() - } - } - - -if is_post() && !theme.comments.lazyload && theme.comments.count && theme.comments.use[0] === 'Disqusjs' - script. - if (window.DISQUSWIDGETS === undefined) { - var d = document, s = d.createElement('script'); - s.src = 'https://!{theme.disqus.shortname}.disqus.com/count.js'; - s.id = 'dsq-count-scr'; - (d.head || d.body).appendChild(s); - } else { - DISQUSWIDGETS.getCount({reset: true}); - } - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/facebook_comments.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/facebook_comments.pug deleted file mode 100755 index cf28c3450..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/facebook_comments.pug +++ /dev/null @@ -1,26 +0,0 @@ -#fb-root -script. - function loadFBComment () { - var themeNow = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light' - document.getElementsByClassName('fb-comments')[0].setAttribute('data-colorscheme',themeNow) - - if (typeof FB === 'object') FB.XFBML.parse() - else { - let ele = document.createElement('script') - ele.setAttribute('src','https://connect.facebook.net/!{theme.facebook_comments.lang}/sdk.js#xfbml=1&version=v7.0') - ele.setAttribute('async', 'true') - ele.setAttribute('defer', 'true') - ele.setAttribute('crossorigin', 'anonymous') - document.getElementById('fb-root').insertAdjacentElement('afterbegin',ele) - } - } - - if ('!{theme.comments.use[0]}' === 'Facebook Comments' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.querySelector('#post-comment .fb-comments'), loadFBComment) - else loadFBComment() - } else { - function loadOtherComment () { - loadFBComment() - } - } - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/gitalk.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/gitalk.pug deleted file mode 100755 index 08bc42228..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/gitalk.pug +++ /dev/null @@ -1,52 +0,0 @@ -script. - function addGitalkSource () { - const ele = document.createElement('link') - ele.rel = 'stylesheet' - ele.href= '!{url_for(theme.CDN.gitalk_css)}' - document.getElementsByTagName('head')[0].appendChild(ele) - } - - function loadGitalk () { - function initGitalk () { - var gitalk = new Gitalk({ - clientID: '!{theme.gitalk.client_id}', - clientSecret: '!{theme.gitalk.client_secret}', - repo: '!{theme.gitalk.repo}', - owner: '!{theme.gitalk.owner}', - admin: ['!{theme.gitalk.admin}'], - id: '!{md5(page.path)}', - language: '!{theme.gitalk.language}', - perPage: !{theme.gitalk.perPage}, - distractionFreeMode: !{theme.gitalk.distractionFreeMode}, - pagerDirection: '!{theme.gitalk.pagerDirection}', - createIssueManually: !{theme.gitalk.createIssueManually}, - updateCountCallback: commentCount - }) - gitalk.render('gitalk-container') - } - - if (typeof Gitalk === 'function') initGitalk() - else { - addGitalkSource() - $.getScript('!{url_for(theme.CDN.gitalk)}', initGitalk) - } - } - - function commentCount(n){ - let isCommentCount = document.querySelector('#post-meta .gitalk-comment-count') - if (isCommentCount) { - isCommentCount.innerHTML= n - } - } - - if ('!{theme.comments.use[0]}' === 'Gitalk' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('gitalk-container'), loadGitalk) - else loadGitalk() - } else { - function loadOtherComment () { - loadGitalk() - } - } - - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/index.pug deleted file mode 100755 index 9f8b4b687..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/index.pug +++ /dev/null @@ -1,38 +0,0 @@ -- let defaultComment = theme.comments.use[0] -hr -#post-comment - .comment-head - .comment-headline - i.fas.fa-comments.fa-fw - span= ' ' + _p('comment') - - if theme.comments.use.length > 1 - #comment-switch - span.first-comment=defaultComment - span.switch-btn - span.second-comment=theme.comments.use[1] - - - .comment-wrap - each name in theme.comments.use - div - case name - when 'Disqus' - #disqus_thread - when 'Valine' - #vcomment.vcomment - when 'Disqusjs' - #disqus_thread - when 'Livere' - #lv-container(data-id="city" data-uid=theme.livere.uid) - when 'Gitalk' - #gitalk-container - when 'Utterances' - #utterances-wrap - when 'Twikoo' - #twikoo - when 'Facebook Comments' - .fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light' - data-numposts= theme.facebook_comments.pageSize || 10 - data-order-by= theme.facebook_comments.order_by || 'social' - data-width="100%") diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/js.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/js.pug deleted file mode 100755 index 07a6f124a..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/js.pug +++ /dev/null @@ -1,18 +0,0 @@ -each name in theme.comments.use - case name - when 'Valine' - !=partial('includes/third-party/comments/valine', {}, {cache:theme.fragment_cache}) - when 'Disqus' - include ./disqus.pug - when 'Disqusjs' - include ./disqusjs.pug - when 'Livere' - !=partial('includes/third-party/comments/livere', {}, {cache:theme.fragment_cache}) - when 'Gitalk' - include ./gitalk.pug - when 'Utterances' - !=partial('includes/third-party/comments/utterances', {}, {cache:theme.fragment_cache}) - when 'Twikoo' - !=partial('includes/third-party/comments/twikoo', {}, {cache:theme.fragment_cache}) - when 'Facebook Comments' - !=partial('includes/third-party/comments/facebook_comments', {}, {cache:theme.fragment_cache}) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/livere.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/livere.pug deleted file mode 100755 index 85b6d783f..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/livere.pug +++ /dev/null @@ -1,26 +0,0 @@ -script. - function loadLivere () { - if (typeof LivereTower === 'object') { - window.LivereTower.init() - } - else { - (function(d, s) { - var j, e = d.getElementsByTagName(s)[0]; - if (typeof LivereTower === 'function') { return; } - j = d.createElement(s); - j.src = 'https://cdn-city.livere.com/js/embed.dist.js'; - j.async = true; - e.parentNode.insertBefore(j, e); - })(document, 'script'); - } - } - - if ('!{theme.comments.use[0]}' === 'Livere' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere) - else loadLivere() - } - else { - function loadOtherComment () { - loadLivere() - } - } \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/twikoo.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/twikoo.pug deleted file mode 100755 index 1e49ecdc1..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/twikoo.pug +++ /dev/null @@ -1,24 +0,0 @@ -script. - function loadTwikoo () { - function init () { - twikoo.init({ - envId: '!{theme.twikoo}' - }) - } - - if (typeof twikoo.init === 'function') { - init() - } else { - $.getScript('!{theme.CDN.twikoo}', init) - } - } - - if ('!{theme.comments.use[0]}' === 'Twikoo' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('twikoo'), loadTwikoo) - else loadTwikoo() - } else { - function loadOtherComment () { - loadTwikoo() - } - } - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/utterances.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/utterances.pug deleted file mode 100755 index dbd422f13..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/utterances.pug +++ /dev/null @@ -1,34 +0,0 @@ -script. - function loadUtterances () { - let ele = document.createElement('script') - ele.setAttribute('id', 'utterances_comment') - ele.setAttribute('src', '!{url_for(theme.CDN.utterances)}') - ele.setAttribute('repo', '!{theme.utterances.repo}') - ele.setAttribute('issue-term', '!{theme.utterances.issue_term}') - let nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '#{theme.utterances.dark_theme}' : '#{theme.utterances.light_theme}' - ele.setAttribute('theme', nowTheme) - ele.setAttribute('crossorigin', 'anonymous') - ele.setAttribute('async', 'true') - document.getElementById('utterances-wrap').insertAdjacentElement('afterbegin',ele) - } - - function utterancesTheme () { - if (document.querySelector('.utterances-frame')) { - const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '#{theme.utterances.dark_theme}' : '#{theme.utterances.light_theme}' - const message = { - type: 'set-theme', - theme: theme - }; - const iframe = document.querySelector('.utterances-frame'); - iframe.contentWindow.postMessage(message, 'https://utteranc.es'); - } - } - - if ('!{theme.comments.use[0]}' === 'Utterances' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('utterances-wrap'), loadUtterances) - else loadUtterances() - } else { - function loadOtherComment () { - loadUtterances() - } - } diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/valine.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/valine.pug deleted file mode 100755 index 6e09a812c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/comments/valine.pug +++ /dev/null @@ -1,51 +0,0 @@ -- let option = theme.valine.option ? JSON.stringify(theme.valine.option) : false -- let emojiMaps = '""' -if site.data.valine - - emojiMaps = JSON.stringify(site.data.valine) - -script. - function loadValine () { - function initValine () { - let initData = { - el: '#vcomment', - appId: '#{theme.valine.appId}', - appKey: '#{theme.valine.appKey}', - placeholder: '#{theme.valine.placeholder}', - avatar: '#{theme.valine.avatar}', - meta: '#{theme.valine.guest_info }'.split(','), - pageSize: '#{theme.valine.pageSize}', - lang: '#{theme.valine.lang}', - recordIP: #{theme.valine.recordIP}, - serverURLs: '#{theme.valine.serverURLs}', - emojiCDN: '#{theme.valine.emojiCDN}', - emojiMaps: !{emojiMaps}, - enableQQ: #{theme.valine.enableQQ}, - path: window.location.pathname, - } - - if (!{Boolean(theme.valine.requiredFields)}) { - initData.requiredFields= ('!{theme.valine.requiredFields}'.split(',')) - } - - if (!{Boolean(option)}) { - const otherData = !{option} - initData = Object.assign({}, initData, otherData) - } - - const valine = new Valine(initData) - } - - if (typeof Valine === 'function') initValine() - else $.getScript('!{url_for(theme.CDN.valine)}', initValine) - } - - if ('!{theme.comments.use[0]}' === 'Valine' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.querySelector('#vcomment'),loadValine) - else setTimeout(() => loadValine(), 0) - } else { - function loadOtherComment () { - loadValine() - } - } - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/effect.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/effect.pug deleted file mode 100755 index 9cd405175..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/effect.pug +++ /dev/null @@ -1,28 +0,0 @@ -if theme.fireworks && theme.fireworks.enable - canvas.fireworks(mobile=`${theme.fireworks.mobile}`) - script(src=url_for(theme.CDN.fireworks)) - -if (theme.canvas_ribbon && theme.canvas_ribbon.enable) - script(defer id="ribbon" src=url_for(theme.CDN.canvas_ribbon) size=theme.canvas_ribbon.size - alpha=theme.canvas_ribbon.alpha zIndex=theme.canvas_ribbon.zIndex mobile=`${theme.canvas_ribbon.mobile}` data-click=`${theme.canvas_ribbon.click_to_change}`) - -if (theme.canvas_fluttering_ribbon && theme.canvas_fluttering_ribbon.enable) - script(defer id="fluttering_ribbon" mobile=`${theme.canvas_fluttering_ribbon.mobile}` src=url_for(theme.CDN.canvas_fluttering_ribbon)) - -if (theme.canvas_nest && theme.canvas_nest.enable) - script#canvas_nest(defer color=theme.canvas_nest.color opacity=theme.canvas_nest.opacity zIndex=theme.canvas_nest.zIndex count=theme.canvas_nest.count mobile=`${theme.canvas_nest.mobile}` src=url_for(theme.CDN.canvas_nest)) - -if theme.activate_power_mode.enable - script(src=url_for(theme.CDN.activate_power_mode)) - script. - POWERMODE.colorful = !{theme.activate_power_mode.colorful}; - POWERMODE.shake = !{theme.activate_power_mode.shake}; - POWERMODE.mobile = !{theme.activate_power_mode.mobile}; - document.body.addEventListener('input', POWERMODE); - -//- 鼠標特效 -if theme.click_heart && theme.click_heart.enable - script#click-heart(src=url_for(theme.CDN.click_heart) async mobile=`${theme.click_heart.mobile}`) - -if theme.ClickShowText && theme.ClickShowText.enable - script#click-show-text(src=url_for(theme.CDN.ClickShowText) async mobile=`${theme.ClickShowText.mobile}`) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/index.pug deleted file mode 100755 index 4ce669233..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/index.pug +++ /dev/null @@ -1,18 +0,0 @@ -if theme.mathjax && theme.mathjax.enable - if theme.mathjax.per_page - if(!is_tag() && !is_category() && !is_archive() && !is_home()) - include ./mathjax.pug - else - if page.mathjax - include ./mathjax.pug - -if theme.katex && theme.katex.enable - if theme.katex.per_page - if(!is_tag() && !is_category() && !is_archive() && !is_home()) - include ./katex.pug - else - if page.katex - include ./katex.pug - -if theme.mermaid.enable - include ./mermaid.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/katex.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/katex.pug deleted file mode 100755 index 6690428ee..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/katex.pug +++ /dev/null @@ -1,7 +0,0 @@ -link(rel="stylesheet" type="text/css" href=theme.CDN.katex) -script(src=url_for(theme.CDN.katex_copytex)) -link(rel="stylesheet" type="text/css" href=theme.CDN.katex_copytex_css) -script. - $(function () { - $('span.katex-display').wrap('
    ') - }) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mathjax.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mathjax.pug deleted file mode 100755 index c87965f2c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mathjax.pug +++ /dev/null @@ -1,53 +0,0 @@ -//- Mathjax 3 -//- http://docs.mathjax.org/en/latest/upgrading/v2.html#changes-in-the-mathjax-api -//- https://github.com/mathjax/MathJax/issues/2209 -//- http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -//- http://docs.mathjax.org/en/latest/web/typeset.html#resetting-automatic-equation-numbering - -script. - if (!window.MathJax) { - window.MathJax = { - loader: { - source: { - '[tex]/amsCd': '[tex]/amscd' - } - }, - tex: { - inlineMath: [ ['$','$'], ["\\(","\\)"]], - tags: 'ams' - }, - options: { - renderActions: { - findScript: [10, doc => { - for (const node of document.querySelectorAll('script[type^="math/tex"]')) { - const display = !!node.type.match(/; *mode=display/) - const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display) - const text = document.createTextNode('') - node.parentNode.replaceChild(text, node) - math.start = {node: text, delim: '', n: 0} - math.end = {node: text, delim: '', n: 0} - doc.math.push(math) - } - }, ''], - addClass: [200,() => { - document.querySelectorAll('mjx-container:not([display=\'true\']').forEach( node => { - const target = node.parentNode - if (!target.classList.contains('has-jax')) { - target.classList.add('mathjax-overflow') - } - }) - }, '', false] - } - } - } - - const script = document.createElement('script') - script.src = '!{theme.CDN.mathjax}' - script.id = 'MathJax-script' - script.async = true - document.head.appendChild(script) - } else { - MathJax.startup.document.state(0) - MathJax.texReset() - MathJax.typeset() - } \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mermaid.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mermaid.pug deleted file mode 100755 index b872a79be..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/math/mermaid.pug +++ /dev/null @@ -1,15 +0,0 @@ -script. - if (document.getElementsByClassName('mermaid').length) { - if (window.mermaidJsLoad) mermaid.init() - else { - $.getScript('!{theme.CDN.mermaid}', function () { - window.mermaidJsLoad = true - mermaid.initialize({ - theme: '!{theme.mermaid.theme}', - }) - !{theme.pjax.enable} && mermaid.init() - }) - } - } - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/disqus-comment.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/disqus-comment.pug deleted file mode 100755 index 5ed18db24..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/disqus-comment.pug +++ /dev/null @@ -1,74 +0,0 @@ -script. - window.addEventListener('load', () => { - const changeContent = (content) => { - if (content === '') return content - - content = content.replace(/<[^>]+>/g,"") // remove html tag - content = content.replace(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|webp)/g, '') // remove image link - content = content.replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi, '') // remove url - - if (content.length > 150) { - content = content.substring(0,150) + '...' - } - return content - } - - const getDisqusComment = () => { - let disqusArray = [] - $.getJSON('https://disqus.com/api/3.0/forums/listPosts.json?forum=!{theme.newest_comments.disqus.forum}&related=thread&limit=!{theme.newest_comments.limit}&api_key=!{theme.newest_comments.disqus.api_key}', function(data){ - $.each(data.response, (i, item) => { - disqusArray.push({ - 'avatar': item.author.avatar.cache, - 'content': changeContent(item.message), - 'nick': item.author.name, - 'url': item.url, - 'date': item.createdAt - }) - }) - // set expiry to 10 min - saveToLocal.set('disqus-newest-comments', JSON.stringify(disqusArray), 10/(60*24)) - generateHtml(disqusArray) - }).fail(()=>{ - const $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= "!{_p('aside.card_newest_comments.error')}" - }) - } - - const generateHtml = array => { - let result = '' - - for (let i = 0; i < array.length; i++) { - result += '
    ' - - if (!{theme.newest_comments.avatar}) { - result += `${array[i].nick}` - } - - result += `
    - ${array[i].content} -
    ${array[i].nick}
    -
    ` - } - - let $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= result - window.pjax && window.pjax.refresh($dom) - } - - const newestCommentInit = () => { - if (document.querySelector('#card-newest-comments .aside-list')) { - const data = saveToLocal.get('disqus-newest-comments') - if (data) { - generateHtml(JSON.parse(data)) - } else { - getDisqusComment() - } - } - } - - newestCommentInit() - document.addEventListener('pjax:complete', newestCommentInit) - }) - - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/github-issues.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/github-issues.pug deleted file mode 100755 index e7e7bbcc3..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/github-issues.pug +++ /dev/null @@ -1,74 +0,0 @@ -script. - window.addEventListener('load', () => { - const changeContent = (content) => { - if (content === '') return content - - content = content.replace(/<[^>]+>/g,"") // remove html tag - content = content.replace(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|webp)/g, '') // remove image link - content = content.replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi, '') // remove url - - if (content.length > 150) { - content = content.substring(0,150) + '...' - } - return content - } - - const getGithubIssues = () => { - let githubArray = [] - $.getJSON('https://api.github.com/repos/!{theme.newest_comments.github_issues.repo}/issues/comments?sort=updated&direction=desc&per_page=!{theme.newest_comments.limit}&page=1', (data) => { - $.each(data, (index, item) => { - githubArray.push({ - 'avatar': item.user.avatar_url, - 'content': changeContent(item.body), - 'nick': item.user.login, - 'url': item.html_url, - 'date': item.updated_at - }) - }) - saveToLocal.set('github-newest-comments', JSON.stringify(githubArray), 10/(60*24)) - generateHtml(githubArray) - }).fail(()=>{ - const $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= "!{_p('aside.card_newest_comments.error')}" - }) - } - - const generateHtml = array => { - let result = '' - - for (let i = 0; i < array.length; i++) { - result += '
    ' - - if (!{theme.newest_comments.avatar}) { - result += `${array[i].nick}` - } - - result += `
    - ${array[i].content} -
    ${array[i].nick}
    -
    ` - } - - let $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= result - window.pjax && window.pjax.refresh($dom) - } - - const newestCommentInit = () => { - if (document.querySelector('#card-newest-comments .aside-list')) { - const data = saveToLocal.get('github-newest-comments') - if (data) { - generateHtml(JSON.parse(data)) - } else { - getGithubIssues() - } - } - } - - newestCommentInit() - document.addEventListener('pjax:complete', newestCommentInit) - }) - - - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/index.pug deleted file mode 100755 index 24b51a725..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/index.pug +++ /dev/null @@ -1,6 +0,0 @@ -if theme.newest_comments.leancloud.enable - include ./leancloud.pug -else if theme.newest_comments.github_issues.enable - include ./github-issues.pug -else if theme.newest_comments.disqus.enable - include ./disqus-comment.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/leancloud.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/leancloud.pug deleted file mode 100755 index 2e6910a3c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/newest-comments/leancloud.pug +++ /dev/null @@ -1,99 +0,0 @@ -script(src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.17.0/js/md5.min.js") -script. - window.addEventListener('load', () => { - const changeContent = (content) => { - if (content === '') return content - - content = content.replace(/<[^>]+>/g,"") // remove html tag - content = content.replace(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|webp)/g, '') // remove image link - content = content.replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi, '') // remove url - - if (content.length > 150) { - content = content.substring(0,150) + '...' - } - return content - } - - const getIcon = (icon, mail) => { - if (icon) return icon - let defaultIcon = '!{ theme.newest_comments.leancloud.default_avatar ? `?d=${theme.newest_comments.leancloud.default_avatar}` : ''}' - let iconUrl = `https://gravatar.loli.net/avatar/${md5(mail.toLowerCase()) + defaultIcon}` - return iconUrl - } - - const generateHtml = array => { - let result = '' - - for (let i = 0; i < array.length; i++) { - result += '
    ' - - if (!{theme.newest_comments.avatar}) { - result += `${array[i].nick}` - } - - result += `
    - ${array[i].content} -
    ${array[i].nick}
    -
    ` - } - - let $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= result - window.pjax && window.pjax.refresh($dom) - } - - const getValineData = () => { - let serverURL = '' - if (!{Boolean(theme.newest_comments.leancloud.serverURL)}) { - serverURL = '!{theme.newest_comments.leancloud.serverURL}' - } else { - serverURL = 'https://!{theme.newest_comments.leancloud.appId.substring(0,8)}.api.lncldglobal.com' - } - - var settings = { - "url": `${serverURL}/1.1/classes/Comment?limit=!{theme.newest_comments.limit}&order=-createdAt`, - "method": "GET", - "timeout": 0, - "headers": { - "X-LC-Id": '!{theme.newest_comments.leancloud.appId}', - "X-LC-Key": '!{theme.newest_comments.leancloud.appKey}', - "Content-Type": "application/json" - }, - } - - $.ajax(settings).done((response) => { - var valineArray = [] - response.results.forEach((e)=>{ - valineArray.push({ - 'avatar': e.QQAvatar, - 'content': changeContent(e.comment), - 'mail': e.mail, - 'nick': e.nick, - 'url': e.url, - 'date': e.createdAt, - }) - }) - - saveToLocal.set('leancloud-newest-comments', JSON.stringify(valineArray), 10/(60*24)) - generateHtml(valineArray) - - }).fail(()=>{ - const $dom = document.querySelector('#card-newest-comments .aside-list') - $dom.innerHTML= "!{_p('aside.card_newest_comments.error')}" - }) - } - - const newestCommentInit = () => { - if (document.querySelector('#card-newest-comments .aside-list')) { - const data = saveToLocal.get('leancloud-newest-comments') - if (data) { - generateHtml(JSON.parse(data)) - } else { - getValineData() - } - } - } - - newestCommentInit() - document.addEventListener('pjax:complete', newestCommentInit) - }) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pangu.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pangu.pug deleted file mode 100755 index 229c69b69..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pangu.pug +++ /dev/null @@ -1,19 +0,0 @@ -script. - function panguFn () { - if (typeof pangu === 'object') pangu.spacingElementById('content-inner') - else { - $.getScript('!{url_for(theme.CDN.pangu)}', () => { - pangu.spacingElementById('content-inner') - }) - } - } - - function panguInit () { - if (!{theme.pangu.field === 'post'}){ - GLOBAL_CONFIG_SITE.isPost && panguFn() - } else { - panguFn() - } - } - - document.addEventListener('DOMContentLoaded', panguFn) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pjax.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pjax.pug deleted file mode 100755 index e3c63bef2..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/pjax.pug +++ /dev/null @@ -1,78 +0,0 @@ -- var pjaxExclude = 'a:not([target="_blank"])' -if theme.pjax.exclude - each val in theme.pjax.exclude - - pjaxExclude = pjaxExclude + `:not([href="${val}"])` - -script(src=url_for(theme.CDN.pjax)) -script. - let pjaxSelectors = [ - 'title', - '#config_change', - '#body-wrap', - '#rightside-config-hide', - '#rightside-config-show', - '.js-pjax' - ] - - if (!{Boolean(theme.Open_Graph_meta && theme.comments.use && theme.comments.use.includes('Livere'))}) { - pjaxSelectors.unshift('meta[property="og:image"]', 'meta[property="og:title"]', 'meta[property="og:url"]') - } - - var pjax = new Pjax({ - elements: '!{pjaxExclude}', - selectors: pjaxSelectors, - cacheBust: false, - analytics: !{theme.google_analytics ? true : false}, - scrollRestoration: false - }) - - document.addEventListener('pjax:complete', function () { - window.refreshFn() - - $('script[data-pjax]').each(function () { - $(this).parent().append($(this).remove()) - }) - - GLOBAL_CONFIG.islazyload && window.lazyLoadInstance.update() - - typeof chatBtnFn === 'function' && chatBtnFn() - typeof panguInit === 'function' && panguInit() - - if (typeof gtag === 'function') { - gtag('config', '!{theme.google_analytics}', {'page_path': window.location.pathname}); - } - - typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting() - - // Analytics - if (!{theme.tencent_analytics ? true : false}) { - MtaH5.pgv() - } - - // prismjs - typeof Prism === 'object' && Prism.highlightAll() - - typeof preloader === 'object' && preloader.endLoading() - }) - - - document.addEventListener('pjax:send', function () { - typeof preloader === 'object' && preloader.initLoading() - - if (window.aplayers) { - for (let i = 0; i < window.aplayers.length; i++) { - if (!window.aplayers[i].options.fixed) { - window.aplayers[i].destroy() - } - } - } - - typeof typed === 'object' && typed.destroy() - - $(window).off('scroll') - - //reset readmode - $('body').hasClass('read-mode') && $('body').removeClass('read-mode') - - }) - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/prismjs.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/prismjs.pug deleted file mode 100755 index b277e25dc..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/prismjs.pug +++ /dev/null @@ -1,5 +0,0 @@ -if config.prismjs && config.prismjs.enable && !config.prismjs.preprocess - script(src=url_for(theme.CDN.prismjs_js)) - script(src=url_for(theme.CDN.prismjs_autoloader)) - if config.prismjs.line_number - script(src=url_for(theme.CDN.prismjs_lineNumber_js)) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/algolia.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/algolia.pug deleted file mode 100755 index 6d22886b1..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/algolia.pug +++ /dev/null @@ -1,13 +0,0 @@ -#algolia-search - .search-dialog - #algolia-search-title.search-dialog__title Algolia - #algolia-input-panel - #algolia-search-input - hr - #algolia-search-results - #algolia-hits - #algolia-pagination - #algolia-stats - span.search-close-button - i.fas.fa-times - #search-mask diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/index.pug deleted file mode 100755 index c1bd03a63..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/index.pug +++ /dev/null @@ -1,4 +0,0 @@ -if theme.algolia_search.enable - include ./algolia.pug -else if theme.local_search.enable - include ./local-search.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/local-search.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/local-search.pug deleted file mode 100755 index 69570a23c..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/search/local-search.pug +++ /dev/null @@ -1,24 +0,0 @@ -#local-search - .search-dialog - #local-search-title.search-dialog__title=_p("local_search.label") - #local-input-panel - #local-search-input - .local-search-box - input(placeholder=_p("local_search.input_placeholder") type="text").local-search-box--input - hr - #local-search-results - #local-hits - #local-stats - #hr.local-search-stats__hr - case config.language - when "zh-CN" - span=_p("local_search.by") - | #[a(href="https://github.com/wzpan/hexo-generator-search" style={'color': '#49B1F5'}) hexo-generator-search] - | #[span=_p("local_search.powered")] - when "en" - default - span=_p("local_search.powered_by") - | #[a(href="https://github.com/wzpan/hexo-generator-search" style={'color': '#49B1F5'}) hexo-generator-search] - span.search-close-button - i.fas.fa-times - #search-mask \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/add-this.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/add-this.pug deleted file mode 100755 index ab44267d9..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/add-this.pug +++ /dev/null @@ -1,2 +0,0 @@ -.addthis_inline_share_toolbox -script(src=`//s7.addthis.com/js/300/addthis_widget.js#pubid=${theme.addThis.pubid}` async) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/addtoany.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/addtoany.pug deleted file mode 100755 index a0a7aa291..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/addtoany.pug +++ /dev/null @@ -1,10 +0,0 @@ -.addtoany - .a2a_kit.a2a_kit_size_32.a2a_default_style - - let addtoanyItem = theme.addtoany.item.split(',') - each name in addtoanyItem - a(class="a2a_button_" + name) - - a.a2a_dd(href="https://www.addtoany.com/share") -script(async src=url_for(theme.CDN.addtoany)) - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/index.pug deleted file mode 100755 index 41892a6a5..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/index.pug +++ /dev/null @@ -1,7 +0,0 @@ -.post_share - if theme.addThis.enable - !=partial('includes/third-party/share/add-this', {}, {cache:theme.fragment_cache}) - else if theme.sharejs.enable - include ./share-js.pug - else if theme.addtoany.enable - !=partial('includes/third-party/share/addtoany', {}, {cache:theme.fragment_cache}) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/share-js.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/share-js.pug deleted file mode 100755 index 538c90529..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/share/share-js.pug +++ /dev/null @@ -1,3 +0,0 @@ -.social-share(data-image=url_for(page.cover|| theme.avatar.img) data-sites= theme.sharejs.sites) -link(rel="stylesheet" href=url_for(theme.CDN.sharejs_css)) -script(src=url_for(theme.CDN.sharejs) defer) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/subtitle.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/subtitle.pug deleted file mode 100755 index ad995cc75..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/third-party/subtitle.pug +++ /dev/null @@ -1,138 +0,0 @@ -- var source = theme.subtitle.source -case source - when 1 - script. - function subtitleType () { - $.getJSON('https://api.btstu.cn/yan/api.php?charset=utf-8&encode=json',function (data) { - if (!{theme.subtitle.effect}) { - var sub = "!{theme.subtitle.sub}".length == 0 ? new Array() : "!{theme.subtitle.sub}".split(',') - var both = sub.unshift(data.text) - typed = new Typed('#subtitle', { - strings: sub, - startDelay: 300, - typeSpeed: 150, - loop: !{theme.subtitle.loop}, - backSpeed: 50, - }) - } else { - document.getElementById('subtitle').innerHTML = data.text - } - }) - } - - if (!{theme.subtitle.effect}) { - if (typeof Typed === 'function') subtitleType() - else $.getScript('!{url_for(theme.CDN.typed)}', subtitleType) - } else { - subtitleType() - } - - when 2 - script. - function subtitleType () { - $.getJSON('https://v1.hitokoto.cn', function (data) { - if (!{theme.subtitle.effect}) { - var from = '出自 ' + data.from - var sub = "!{theme.subtitle.sub}".length == 0 ? new Array() : "!{theme.subtitle.sub}".split(',') - var both = sub.unshift(data.hitokoto, from) - var typed = new Typed('#subtitle', { - strings: sub, - startDelay: 300, - typeSpeed: 150, - loop: !{theme.subtitle.loop}, - backSpeed: 50, - }) - } else { - document.getElementById('subtitle').innerHTML = data.hitokoto - } - }) - } - - if (!{theme.subtitle.effect}) { - if (typeof Typed === 'function') subtitleType() - else $.getScript('!{url_for(theme.CDN.typed)}', subtitleType) - } else { - subtitleType() - } - - when 3 - script. - function subtitleType () { - $.getScript('http://yijuzhan.com/api/word.php?m=js', function () { - var con = str[0] - if (!{theme.subtitle.effect}) { - var from = '出自 ' + str[1] - var sub = "!{theme.subtitle.sub}".length == 0 ? new Array() : "!{theme.subtitle.sub}".split(',') - var both = sub.unshift(con, from) - var typed = new Typed('#subtitle', { - strings: sub, - startDelay: 300, - typeSpeed: 150, - loop: !{theme.subtitle.loop}, - backSpeed: 50, - }) - } else { - document.getElementById('subtitle').innerHTML = con - } - }) - } - - if (!{theme.subtitle.effect}) { - if (typeof Typed === 'function') subtitleType() - else $.getScript('!{url_for(theme.CDN.typed)}', subtitleType) - } else { - subtitleType() - } - - when 4 - script. - function subtitleType () { - $.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js',function () { - jinrishici.load(function (result) { - if (!{theme.subtitle.effect}) { - var sub = "!{theme.subtitle.sub}".length == 0 ? new Array() : "!{theme.subtitle.sub}".split(',') - var content = result.data.content - var both = sub.unshift(content) - var typed = new Typed('#subtitle', { - strings: sub, - startDelay: 300, - typeSpeed: 150, - loop: !{theme.subtitle.loop}, - backSpeed: 50, - }) - } else { - document.getElementById('subtitle').innerHTML = result.data.content - } - }) - }) - } - - if (!{theme.subtitle.effect}) { - if (typeof Typed === 'function') subtitleType() - else $.getScript('!{url_for(theme.CDN.typed)}', subtitleType) - } else { - subtitleType() - } - - default - script. - function subtitleType () { - if (!{theme.subtitle.effect}) { - var typed = new Typed("#subtitle", { - strings: "!{theme.subtitle.sub}".split(","), - startDelay: 300, - typeSpeed: 150, - loop: !{theme.subtitle.loop}, - backSpeed: 50 - }) - } else { - document.getElementById("subtitle").innerHTML = '!{theme.subtitle.sub[0]}' - } - } - - if (!{theme.subtitle.effect}) { - if (typeof Typed === 'function') subtitleType() - else $.getScript('!{url_for(theme.CDN.typed)}', subtitleType) - } else { - subtitleType() - } \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_ad.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_ad.pug deleted file mode 100755 index c05f77421..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_ad.pug +++ /dev/null @@ -1,2 +0,0 @@ -.card-widget.card-ad - != theme.ad.aside diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_announcement.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_announcement.pug deleted file mode 100755 index 5f6fbd142..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_announcement.pug +++ /dev/null @@ -1,6 +0,0 @@ -.card-widget.card-announcement - .card-content - .item-headline - i.fas.fa-bullhorn.card-announcement-animation - span= _p('aside.card_announcement') - .announcement_content!= theme.aside.card_announcement.content \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_archives.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_archives.pug deleted file mode 100755 index dfb5775f2..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_archives.pug +++ /dev/null @@ -1,11 +0,0 @@ -.card-widget.card-archives - .card-content - .item-headline - i.fas.fa-archive - span= _p('aside.card_archives') - - - let type = theme.aside.card_archives.type || 'monthly' - - let format = theme.aside.card_archives.format || 'MMMM YYYY' - - let order = theme.aside.card_archives.order || -1 - - let limit = theme.aside.card_archives.limit === 0 ? 0 : theme.aside.card_archives.limit || 8 - != aside_archives({ type:type, format: format, order: order, limit: limit }) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_author.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_author.pug deleted file mode 100755 index b1faefb6a..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_author.pug +++ /dev/null @@ -1,36 +0,0 @@ -.card-widget.card-info - .card-content - .card-info-avatar.is-center - if theme.lazyload.enable - img.avatar-img(data-lazy-src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt="avatar") - else - img.avatar-img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt="avatar") - .author-info__name= config.author - .author-info__description!= theme.aside.card_author.description || config.description - - .card-info-data - if site.posts.length - .card-info-data-item.is-center - a(href=url_for(config.archive_dir) + '/') - .headline= _p('aside.articles') - .length-num= site.posts.length - - if site.tags.length - .card-info-data-item.is-center - a(href=url_for(config.tag_dir) + '/') - .headline= _p('aside.tags') - .length-num= site.tags.length - - if site.categories.length - .card-info-data-item.is-center - a(href=url_for(config.category_dir) + '/') - .headline= _p('aside.categories') - .length-num= site.categories.length - - a#card-info-btn.button--animated(href=theme.aside.card_author.button.link) - i(class=theme.aside.card_author.button.icon) - span=theme.aside.card_author.button.text - - if(theme.social) - .card-info-social-icons.is-center - !=fragment_cache('social', function(){return partial('includes/header/social')}) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_categories.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_categories.pug deleted file mode 100755 index 55c1827bb..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_categories.pug +++ /dev/null @@ -1,9 +0,0 @@ -if site.categories.length - .card-widget.card-categories - .card-content - .item-headline - i.fas.fa-folder-open - span= _p('aside.card_categories') - !=aside_categories({ limit: theme.aside.card_categories.limit === 0 ? 0 : theme.aside.card_categories.limit || 8 , expand: theme.aside.card_categories.expand }) - - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_newest_comment.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_newest_comment.pug deleted file mode 100755 index 76a19aa0e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_newest_comment.pug +++ /dev/null @@ -1,7 +0,0 @@ -.card-widget#card-newest-comments - .card-content - .item-headline - i.fas.fa-bolt - span= _p('aside.card_newest_comments.headline') - .aside-list - span= _p('aside.card_newest_comments.loading_text') diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_post_toc.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_post_toc.pug deleted file mode 100755 index 065a4570d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_post_toc.pug +++ /dev/null @@ -1,13 +0,0 @@ -- let tocNumber = page.toc_number !== undefined ? page.toc_number : theme.toc.number - -#card-toc.card-widget - .card-content - .item-headline - i.fas.fa-stream - span= _p('aside.card_toc') - - if (page.encrypt == true) - .toc-content.toc-div-class(style="display:none")!=toc(page.origin, {list_number: tocNumber}) - else - .toc-content!=toc(page.content, {list_number: tocNumber}) - \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_recent_post.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_recent_post.pug deleted file mode 100755 index 03cba6e11..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_recent_post.pug +++ /dev/null @@ -1,27 +0,0 @@ -.card-widget.card-recent-post - .card-content - .item-headline - i.fas.fa-history - span= _p('aside.card_recent_post') - .aside-list - - let postLimit = theme.aside.card_recent_post.limit === 0 ? site.posts.length : theme.aside.card_recent_post.limit || 5 - - let sort = theme.aside.card_recent_post.sort === 'updated' ? 'updated' : 'date' - - site.posts.sort(sort, -1).limit(postLimit).each(function(article){ - - let link = article.link || article.path - - let title = article.title || _p('no_title') - - let no_cover = article.cover === false || !theme.cover.aside_enable ? 'no-cover' : '' - - let post_cover = article.cover - .aside-list-item(class=no_cover) - if post_cover && theme.cover.aside_enable - a.thumbnail(href=url_for(link) title=title) - if theme.lazyload.enable - img(data-lazy-src=url_for(post_cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title) - else - img(src=url_for(post_cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title) - .content - a.title(href=url_for(link) title=title)= title - if theme.aside.card_recent_post.sort === 'updated' - time(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated)) #[=date(article.updated, config.date_format)] - else - time(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date)) #[=date(article.date, config.date_format)] - - }) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_tags.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_tags.pug deleted file mode 100755 index 4ca6ee607..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_tags.pug +++ /dev/null @@ -1,12 +0,0 @@ -if site.tags.length - .card-widget.card-tags - .card-content - .item-headline - i.fas.fa-tags - span= _p('aside.card_tags') - - - let tagLimit = theme.aside.card_tags.limit === 0 ? 0 : theme.aside.card_tags.limit || 40 - if theme.aside.card_tags.color - .card-tag-cloud!= cloudTags({source: site.tags, minfontsize: 1.1, maxfontsize: 1.5, limit: tagLimit, unit: 'em'}) - else - .card-tag-cloud!= tagcloud({min_font: 1.1, max_font: 1.5, amount: tagLimit , color: true, start_color: '#999', end_color: '#99a9bf', unit: 'em'}) diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_webinfo.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_webinfo.pug deleted file mode 100755 index ab961470e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/card_webinfo.pug +++ /dev/null @@ -1,31 +0,0 @@ -.card-widget.card-webinfo - .card-content - .item-headline - i.fas.fa-chart-line - span= _p('aside.card_webinfo.headline') - .webinfo - if theme.aside.card_webinfo.post_count - .webinfo-item - .item-name= _p('aside.card_webinfo.article_name') + " :" - .item-count= site.posts.length - if theme.runtimeshow.enable - .webinfo-item - .item-name= _p('aside.card_webinfo.runtime.name') + " :" - .item-count#runtimeshow(data-publishDate=date_xml(theme.runtimeshow.publish_date)) - if theme.wordcount.enable && theme.wordcount.total_wordcount - .webinfo-item - .item-name=_p('aside.card_webinfo.site_wordcount') + " :" - .item-count=totalcount(site) - if theme.busuanzi.site_uv - .webinfo-item - .item-name= _p('aside.card_webinfo.site_uv_name') + " :" - .item-count#busuanzi_value_site_uv - if theme.busuanzi.site_pv - .webinfo-item - .item-name= _p('aside.card_webinfo.site_pv_name') + " :" - .item-count#busuanzi_value_site_pv - if theme.aside.card_webinfo.last_push_date - .webinfo-item - .item-name= _p('aside.card_webinfo.last_push_date.name') + " :" - .item-count#last-push-date(data-lastPushDate=date_xml(Date.now())) - diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/index.pug deleted file mode 100755 index ec2a80e1e..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/includes/widget/index.pug +++ /dev/null @@ -1,28 +0,0 @@ -#aside_content.aside_content - if theme.aside.card_author.enable - !=partial('includes/widget/card_author', {}, {cache:theme.fragment_cache}) - if theme.aside.card_announcement.enable - !=partial('includes/widget/card_announcement', {}, {cache:theme.fragment_cache}) - .sticky_layout - if is_post() - if showToc - include ./card_post_toc.pug - if theme.aside.card_recent_post.enable - !=partial('includes/widget/card_recent_post', {}, {cache:theme.fragment_cache}) - if theme.ad && theme.ad.aside - !=partial('includes/widget/card_ad', {}, {cache:theme.fragment_cache}) - else - if theme.aside.card_recent_post.enable - !=partial('includes/widget/card_recent_post', {}, {cache:theme.fragment_cache}) - if theme.ad && theme.ad.aside - !=partial('includes/widget/card_ad', {}, {cache:theme.fragment_cache}) - if theme.newest_comments.enable - !=partial('includes/widget/card_newest_comment', {}, {cache:theme.fragment_cache}) - if theme.aside.card_categories.enable - !=partial('includes/widget/card_categories', {}, {cache:theme.fragment_cache}) - if theme.aside.card_tags.enable - !=partial('includes/widget/card_tags', {}, {cache:theme.fragment_cache}) - if theme.aside.card_archives.enable - !=partial('includes/widget/card_archives', {}, {cache:theme.fragment_cache}) - if theme.aside.card_webinfo.enable - !=partial('includes/widget/card_webinfo', {}, {cache:theme.fragment_cache}) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/index-re.pug b/themes/hexo-theme-butterfly-3.3.0/layout/index-re.pug deleted file mode 100644 index 2655cb4b3..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/index-re.pug +++ /dev/null @@ -1,7 +0,0 @@ -extends includes/layout.pug - -block content - include ./includes/mixins/post-ui.pug - #recent-posts.recent-posts - +postUI - include includes/pagination.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/index.pug b/themes/hexo-theme-butterfly-3.3.0/layout/index.pug deleted file mode 100755 index 152078c84..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/index.pug +++ /dev/null @@ -1,10 +0,0 @@ -extends includes/layout.pug - -block content - include ./includes/mixins/post-ui.pug - #recent-posts.recent-posts - .recent-post-item(style='width:100%; height: auto;') - include gitcalendar.pug - .recent-post-item(style='height:0px; clear:both; margin-top: 0px;') - +postUI - include includes/pagination.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/page.pug b/themes/hexo-theme-butterfly-3.3.0/layout/page.pug deleted file mode 100755 index 474580df7..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/page.pug +++ /dev/null @@ -1,19 +0,0 @@ -extends includes/layout.pug - -block content - #page - case page.type - when 'tags' - include includes/page/tags.pug - when 'link' - include includes/page/flink.pug - when 'categories' - include includes/page/categories.pug - when 'artitalk' - include includes/page/artitalk.pug - default - include includes/page/default-page.pug - - if page.comments !== false && theme.comments && theme.comments.use - - var commentsJsLoad = true - !=partial('includes/third-party/comments/index', {}, {cache:theme.fragment_cache}) \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/post.pug b/themes/hexo-theme-butterfly-3.3.0/layout/post.pug deleted file mode 100755 index c57d99e70..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/post.pug +++ /dev/null @@ -1,31 +0,0 @@ -extends includes/layout.pug - -block content - #post - if top_img === false - include includes/header/post-info.pug - - article#article-container.post-content!=page.content - include includes/post/post-copyright.pug - .tag_share - if (theme.post_meta.post.tags) - .post-meta__tag-list - each item, index in page.tags.data - a(href=url_for(item.path)).post-meta__tags #[=item.name] - include includes/third-party/share/index.pug - - if theme.reward.enable - !=partial('includes/post/reward', {}, {cache:theme.fragment_cache}) - - //- ad - if theme.ad && theme.ad.post - .post-ad!=theme.ad.post - - include includes/pagination.pug - if theme.related_post && theme.related_post.enable - != related_posts(page,site.posts) - - if page.comments !== false && theme.comments && theme.comments.use - - var commentsJsLoad = true - !=partial('includes/third-party/comments/index', {}, {cache:theme.fragment_cache}) - \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/layout/tag.pug b/themes/hexo-theme-butterfly-3.3.0/layout/tag.pug deleted file mode 100755 index 9f99658d5..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/layout/tag.pug +++ /dev/null @@ -1,14 +0,0 @@ -extends includes/layout.pug - -block content - if theme.tag_ui == 'index' - include ./includes/mixins/post-ui.pug - #recent-posts.recent-posts - +postUI - include includes/pagination.pug - else - include ./includes/mixins/article-sort.pug - #tag - .article-sort-title= _p('page.tag') + ' - ' + page.tag - +articleSort(page.posts) - include includes/pagination.pug \ No newline at end of file diff --git a/themes/hexo-theme-butterfly-3.3.0/package.json b/themes/hexo-theme-butterfly-3.3.0/package.json deleted file mode 100755 index 180092b73..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "hexo-theme-butterfly", - "version": "3.3.0", - "description": "A Simple and Card UI Design theme for Hexo", - "main": "package.json", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [ - "hexo", - "theme", - "butterfly", - "Card UI Design", - "Jerry", - "hexo-theme-butterfly" - ], - "repository": { - "type" : "git", - "url" : "https://github.com/jerryc127/hexo-theme-butterfly.git" - }, - "bugs": { - "url": "https://github.com/jerryc127/hexo-theme-butterfly/issues", - "email": "wong@jerryc.me" - }, - "dependencies": { - "hexo-renderer-stylus": "^2.0.1", - "hexo-renderer-pug": "^1.0.0" - }, - "homepage": "https://demo.jerryc.me/", - "author": "Jerry ", - "license": "Apache-2.0" -} diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/events/404.js b/themes/hexo-theme-butterfly-3.3.0/scripts/events/404.js deleted file mode 100755 index c37daae40..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/events/404.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Butterfly - * 404 error page - */ - -'use strict' - -hexo.extend.generator.register('404', function (locals) { - if (!hexo.theme.config.error_404.enable) return - return { - path: '404.html', - data: locals.posts, - layout: ['404'] - } -}) diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/events/init.js b/themes/hexo-theme-butterfly-3.3.0/scripts/events/init.js deleted file mode 100755 index c198e9e80..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/events/init.js +++ /dev/null @@ -1,27 +0,0 @@ -const logger = require('hexo-log')() - -hexo.extend.filter.register('before_generate', () => { - // Get first two digits of the Hexo version number - var hexoVer = hexo.version.replace(/(^.*\..*)\..*/, '$1') - - if (hexoVer < 5) { - logger.error('Please update Hexo to V5.0.0 or higher!') - logger.error('請把 Hexo 升級到 V5.0.0 或更高的版本!') - process.exit(-1) - } - - if (hexo.locals.get) { - const data = hexo.locals.get('data') - if (data && data.butterfly) { - logger.error(" 'butterfly.yml' is deprecated. Please use '_config.butterfly.yml' ") - logger.error(" 'butterfly.yml' 已經棄用,請使用 '_config.butterfly.yml' ") - process.exit(-1) - } - } - - // let stylus to get the hexo highlight config - const themeConfig = hexo.theme.config - const hexoConfig = hexo.config - themeConfig.highlight_settings = hexoConfig.highlight - themeConfig.prismjs_settings = hexoConfig.prismjs -}) diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/events/welcome.js b/themes/hexo-theme-butterfly-3.3.0/scripts/events/welcome.js deleted file mode 100755 index f6650f930..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/events/welcome.js +++ /dev/null @@ -1,17 +0,0 @@ -const logger = require('hexo-log')() - -hexo.on('ready', () => { - const { version } = require('../../package.json') - logger.info(` - =================================================================== - - ##### # # ##### ##### ###### ##### ###### # # # - # # # # # # # # # # # # # - ##### # # # # ##### # # ##### # # - # # # # # # # ##### # # # - # # # # # # # # # # # # - ##### #### # # ###### # # # ###### # - - ${version} - ===================================================================`) -}) diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/filters/post_lazyload.js b/themes/hexo-theme-butterfly-3.3.0/scripts/filters/post_lazyload.js deleted file mode 100755 index c27963324..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/filters/post_lazyload.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Butterfly - * lazyload - * replace src to data-lazy-src - */ - -'use strict' - -const urlFor = require('hexo-util').url_for.bind(hexo) - -hexo.extend.filter.register('after_post_render', data => { - if (!hexo.theme.config.lazyload.enable) return - const bg = hexo.theme.config.lazyload.post ? urlFor(hexo.theme.config.lazyload.post) : 'data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs=' - data.content = data.content.replace(/( yearA === yearB && monthA === monthB - : (yearA, monthA, yearB, monthB) => yearA === yearB - const limit = options.limit - const moreButton = this._p('aside.more_button') - let result = '' - - if (!format) { - format = type === 'monthly' ? 'MMMM YYYY' : 'YYYY' - } - - const posts = this.site.posts.sort('date', order) - if (!posts.length) return result - - const data = [] - let length = 0 - - posts.forEach(post => { - // Clone the date object to avoid pollution - let date = post.date.clone() - - if (timezone) date = date.tz(timezone) - - const year = date.year() - const month = date.month() + 1 - const lastData = data[length - 1] - - if (!lastData || !compareFunc(lastData.year, lastData.month, year, month)) { - if (lang) date = date.locale(lang) - const name = date.format(format) - length = data.push({ - name, - year, - month, - count: 1 - }) - } else { - lastData.count++ - } - }) - - const link = item => { - let url = `${archiveDir}/${item.year}/` - - if (type === 'monthly') { - if (item.month < 10) url += '0' - url += `${item.month}/` - } - - return this.url_for(url) - } - - result += '' - return result -}) - -const toMomentLocale = function (lang) { - if (lang === undefined) { - return undefined - } - - // moment.locale('') equals moment.locale('en') - // moment.locale(null) equals moment.locale('en') - if (!lang || lang === 'en' || lang === 'default') { - return 'en' - } - return lang.toLowerCase().replace('_', '-') -} diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/aside_categories.js b/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/aside_categories.js deleted file mode 100755 index a787bad5b..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/aside_categories.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Butterfly - * for aside categories - */ - -'use strict' - -hexo.extend.helper.register('aside_categories', function (categories, options) { - if (!options && (!categories || !Object.prototype.hasOwnProperty.call(categories, 'length')) - ) { - options = categories - categories = this.site.categories - } - - if (!categories || !categories.length) return '' - options = options || {} - const { config } = this - const showCount = Object.prototype.hasOwnProperty.call(options, 'show_count') - ? options.show_count - : true - const depth = options.depth ? parseInt(options.depth, 10) : 0 - const orderby = options.orderby || 'name' - const order = options.order || 1 - const categoryDir = this.url_for(config.category_dir) - const limit = options.limit === 0 ? categories.length : options.limit - const isExpand = options.expand !== 'none' - const expandClass = isExpand && options.expand === true ? 'expand' : '' - - const buttonLabel = this._p('aside.more_button') - const prepareQuery = (parent) => { - const query = {} - if (parent) { query.parent = parent } else { query.parent = { $exists: false } } - return categories.find(query).sort(orderby, order).filter((cat) => cat.length) - } - - const hierarchicalList = (t, level, parent, topparent = true) => { - let result = '' - const isTopParent = topparent - if (t > 0) { - prepareQuery(parent).forEach((cat, i) => { - if (t > 0) { - t = t - 1 - let child - if (!depth || level + 1 < depth) { - const childList = hierarchicalList(t, level + 1, cat._id, false) - child = childList[0] - t = childList[1] - } - - const parentClass = isExpand && isTopParent && child ? 'parent' : '' - - result += `
  • ` - - result += `` - - result += `${cat.name}` - - if (showCount) { - result += `${cat.length}` - } - - if (isExpand && isTopParent && child) { - result += `` - } - - result += '' - - result += '
  • ' - - if (child) { - result += `
      ${child}
    ` - } - } - }) - } - - return [result, t] - } - - const list = hierarchicalList(limit, 0) - - const moreButton = function () { - let moreHtml = '' - if (categories.length <= limit) return '' - moreHtml += '
  • ' - moreHtml += ` - ${buttonLabel}
  • ` - - return moreHtml - } - - return `
      - ${list[0]} - ${moreButton()} -
    ` -}) diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/page.js b/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/page.js deleted file mode 100755 index fd441582d..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/page.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Butterfly - * @example - * page_description() - * cloudTags(source, minfontsize, maxfontsize, limit) - */ - -'use strict' - -const { stripHTML, escapeHTML, prettyUrls } = require('hexo-util') -const crypto = require('crypto') - -hexo.extend.helper.register('page_description', function () { - const { config, page } = this - let description = page.description || page.content || page.title || config.description - - if (description) { - description = escapeHTML(stripHTML(description).substring(0, 200) - .trim() - ).replace(/\n/g, ' ') - return description - } -}) - -hexo.extend.helper.register('cloudTags', function (options = {}) { - const env = this - let source = options.source - const minfontsize = options.minfontsize - const maxfontsize = options.maxfontsize - const limit = options.limit - const unit = options.unit || 'px' - - let result = '' - if (limit > 0) { - source = source.limit(limit) - } - - const sizes = [] - source.sort('length').forEach(tag => { - const { length } = tag - if (sizes.includes(length)) return - sizes.push(length) - }) - - const length = sizes.length - 1 - source.forEach(tag => { - const ratio = length ? sizes.indexOf(tag.length) / length : 0 - const size = minfontsize + ((maxfontsize - minfontsize) * ratio) - let style = `font-size: ${parseFloat(size.toFixed(2))}${unit};` - const color = 'rgb(' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ')' // 0,0,0 -> 200,200,200 - style += ` color: ${color}` - result += `${tag.name}` - }) - return result -}) - -hexo.extend.helper.register('urlNoIndex', function () { - return prettyUrls(this.url, { trailing_index: false, trailing_html: false }) -}) - -hexo.extend.helper.register('md5', function (path) { - return crypto.createHash('md5').update(decodeURI(this.url_for(path))).digest('hex') -}) - -hexo.extend.helper.register('injectHtml', function (data) { - let result = '' - if (!data) return '' - for (let i = 0; i < data.length; i++) { - result += data[i] - } - return result -}) diff --git a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/related_post.js b/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/related_post.js deleted file mode 100755 index 24992b4fa..000000000 --- a/themes/hexo-theme-butterfly-3.3.0/scripts/helpers/related_post.js +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Butterfly - * Related Posts - * According the tag - */ - -'use strict' - -hexo.extend.helper.register('related_posts', function (currentPost, allPosts) { - let relatedPosts = [] - currentPost.tags.forEach(function (tag) { - allPosts.forEach(function (post) { - if (isTagRelated(tag.name, post.tags)) { - const relatedPost = { - title: post.title, - path: post.path, - cover: post.cover, - randomcover: post.randomcover, - weight: 1, - updated: post.updated, - created: post.date - } - const index = findItem(relatedPosts, 'path', post.path) - if (index !== -1) { - relatedPosts[index].weight += 1 - } else { - if (currentPost.path !== post.path) { - relatedPosts.push(relatedPost) - } - } - } - }) - }) - if (relatedPosts.length === 0) { - return '' - } - let result = '' - const hexoConfig = hexo.config - const config = hexo.theme.config - - const limitNum = config.related_post.limit || 6 - const dateType = config.related_post.date_type || 'created' - const headlineLang = this._p('post.recommend') - const lazySrc = config.lazyload.enable ? 'data-lazy-src' : 'src' - - relatedPosts = relatedPosts.sort(compare('weight')) - - if (relatedPosts.length > 0) { - result += '