Create by jsliang on 2019-2-11 15:30:34
Recently revised in 2019-3-17 21:30:36
Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 小伙伴们的 star 是我持续更新的动力!GitHub 地址
并不是只有特定的季节才能跑路,只因为人跑得多了,这条路就定下来了。
金三银四跳槽季,jsliang 于 2019年2月11日 写下了这篇文章,开始准备自己的面试之旅。
至 2019年3月17日 为止,jsliang 搭建出个人的面试知识体系,海量地翻阅了一些前辈留下的资料,结合个人需求进行了几场面试,从而进一步完善该文章并进行了发表,希望对准备跳槽或者正在跳槽中的小伙伴有所帮助。
一 目录
不折腾的前端,和咸鱼有什么区别
二 前言
请时刻准备好自己的简历,不管是互联网经济不佳面临裁员,还是因为公司内部斗争严重心乱如麻,还是因为厌倦了目前的一切……只有随时更新自己,把自己的简历准备好,你才知道哪一刻跑路是最佳选择。
2.1 自我介绍
Hello 小伙伴们好,我叫梁峻荣,网名叫 jsliang,由于崇拜前端大佬 技术胖(jspang)的原因,又因为自己学的是前端(JavaScript),所以给自己取了个 jsliang(JavaScriptLiang) 的网名,希望自己能通过建立自己的前端知识体系,从而在前端路上走得更远。并将自己的经验分享给小伙伴,携手小伙伴们一起前行。
下面是讲讲个人故事:
首先,jsliang 高考后的暑期就听大学师兄的建议,开始学习编程,那时候学习了 C 语言,觉得世界上最神奇的事情不过如此,敲两下键盘,按下回车,电脑就会一闪一闪地响应我们!于是在大学的时候,陆陆续续学过 C、C#、.Net 等……。
-_-|| 由于学得都是基础,又都还给老师了,在这里就不多累述了。
然后,在大二就开始接触 HTML,那时候选修了个《网页设计基础》,跟着老师做了个只有几个页面的静态网站。在大三的时候,参加了学校的特训班,分角色按流程从头到尾做了个包含角色管理、购物等功能的网站。同时,由于在特训班的时候,看到后端使用 ThinkPHP(简称 TP),觉得蛮不错的,于是自己捣鼓,使用 TP 3.2.3 + Bootstrap 3 + MySQL 打造了自己的博客(已下线)。
接着,由于选修了门 Node.js 的课,所以也跟着大佬的步伐接触了下 Vue、Koa 这些,那时候对 npm 等诸多不懂,为了折腾这个,我的前端世界自此打开了个大门。
最后,我在自己的毕业设计中使用 Node.js + Vue + ElementUI + MongoDB 打造了个校园外卖、快递代拿等功能的社区单页应用。
在 2018 年 5 月的时候,家里催促,于是直接出来面试。不像其他大佬的毕业等于失业,很幸运地 jsliang 面试第一家就给了 offer,于是就进了这家公司,那时候感觉自己前面的大学生活白过了,只懂 ES5、jQuery、HTML/HTML5、CSS/CSS3 的皮毛。
在熟悉了三个月的业务,公司给的任务能顺利完成后,我觉得自己不够努力:外面的前端翻天覆地,我的技术却只是 jQuery is all!
于是 2018 年 8 月,jsliang 开始写 Markdown,将 5 月份到 8 月份记录到 Word 文档上的笔记整理成了 jsliang 的文档库,并在 jsliang 的掘金 上发表了第一篇文章。
18 年 8 月至今,jsliang 大致经历了以下这些:
- 学 Webpack,并用 Webpack 构建一个多页面配置。然后幸运的是,刚好碰到公司的一个仿站任务,于是整个前端小组直接用了我的 Webpack + jQuery + VS Code 的 Live Share 套路进行协同开发!
- 学 微信小程序,并将它应用到电信开发的微信小程序项目翼小微中。
- 学 ECharts 报表,并用它做了个 Vue + ECharts 的报表,来展示爱音乐公司的运营数据。
- 学 Node,然后搭了个企业网站(company.jsliang.top),并写了篇小文章(目前最高成就,获得了 1100 多赞)。
- 学 Vue,由于之前学的 Vue,在工作上有好多没有,逐渐淡忘了,所以从基础开始,准备写一套《Vue 从 0 到 1》。
以上,就是 jsliang 的编程生涯。
今儿,在这里写写 jsliang 为了跳槽,根据个人想法进行的一些前端面试资料整理,小伙伴们觉得不错的点个赞或者去 GitHub 点个 star,觉得有误请指出,谢谢~
2.2 跳槽原委
马老板曾经说过,跳槽有两个原因:
- 钱没给到位。
- 心被委屈了。
首先,如果非要给 jsliang 我自己的跳槽定个位,那肯定是钱没给到位,劳动与产出不成正比。在我 2018 年 5 月入职前,与人事的交谈中了解到每年的 8 月和 2 月可以提薪,当初的技术栈是:HTML、CSS、ES5。
然后,2018 年 6 月到 2019 年 1 月,学习并应用到工作中的技术有:
- jQuery
- Webpack
- JSP
- 微信小程序
- Vue
- ECharts
其中 2018 年 8 月刚转正,也不敢说自己技术进步很大,也不敢说自己项目贡献很大,为公司谋了多大利益,所以没有提薪想法。
2019 年 1 月底感觉自己项目也做了,凌晨 4/5/6 点的体育西路也看过了,技术也提升了,于是跟人事交谈,期望 2 月能加薪,人事表示年终述职演讲得好的给提薪,2 月开工的时候表示提薪名单没我份……
你没看错,提薪全靠 PPT。PPT 里提高了不给,没提就是没有。
当初想法很简单,你随便加个 5/600 我也满足了。
最后,jsliang 曾跟项目总监私下谈话,建议可以发展一些新产品,这样公司或许能获取一些新收入,我也可以进一步挑战我的技术。但是,由于我司是个老牌子公司,并且大部分依赖于接手电信项目进行扩张……
enm...所以心也委屈了。
在 2018 的努力下,GitHub 破 600 近 700 star,掘金破 10 万阅读量,3000 粉丝:
GitHub 见证:点击查看
掘金见证:点击查看
2.3 进击目标
- 目标城市:广州
- 目标薪资:10K - 15K
- 目标技术:
- 1. 熟悉 HTML/HTML5、CSS/CSS3、ES5/ES6。
- 2. 了解 OOP 概念,并尝试在工作中使用过 OOP 技巧。
- 3. 对 MVC/MVVM 架构有一定了解,如有 Vue/React/Angular 或者 微信小程序开发经验更佳。
- 4. 使用过 Bootstrap 或者 ElementUI 等 UI 库,并对前端 UI 库有一定的个人理解。
- 5. 了解 Git、Webpack 等工具。
- 6. 对 Java、Node.js 等后端编程有一定了解。
- 7. 一年及以上工作经验。
- 复制代码
- 广州前端分析:
- 广州 13K 薪资要求:
- 2/3 年工作经验。
- 熟悉 OOP,并能在工作中使用,且能独立开发插件等……
- 你可以不懂 Vue,但 React 你必须得会!
- 广州 15k+ 薪资要求:
- 5 年+ 工作经验。
- 全能,懂前端,熟悉后端,能写文档……
- 带领过小队进行开发。
- 广州异常偏科公司:
- 要求必须懂后端。
- 要求必须懂安卓或者 IOS 开发。
- 要求必须精通 jQuery 全家桶(jQuery UI、jQuery Mobile 等……)。
该分析数据来自 Boss 直聘
2.4 开篇点题
本文的知识点将涉及 HTML、CSS、JS、HTTP、Vue、Webpack、打包工具、性能优化等,没有前置条件,看得懂可以瞅瞅复习下,看不懂可以瞅瞅学习下。
关于面试,在这记下慕课网视频看到的,个人非常认同的三个问答:
- 问:拿到一个面试题,第一时间看到什么? 答:考点
- 问:如何看待网上搜出来的永远看不完的题海? 答:不变应万变
- 问:如何对待面试题? 答:题目到知识再到题目
然后在复习面试题的过程中,个人有些小看法:
- 个人感言一:为什么我总是比不上别人优秀?
当编写业务代码中,碰到某个业务 bug 时,我会习惯性地百度这个业务 bug,看看网友是怎么解决的。但是,学霸级的程序猿,会多走一步,他们会思考产生这个业务 bug 的底层原因是什么,下次碰到类似的是如何应用该技术解决。所以,日积月累,我的确比不上人家了。
- 个人感言二:辞职并不是件便捷的事。
way 1:面试成功,跟自己公司递辞呈,走流程,同时跟对面 hr 申请一个月后入职。
way 2:面试成功,跟自己公司递辞呈,询问能不能快速离职,收到回复跟对面 hr 确认时间。【推荐】
way 3:先递辞呈,同时面试,面试成功的,一律申请走完原公司一个月的流程之后的日子入职。
jsliang 于 2 月底拿到 offer 并递交辞呈,3 月 - 4 月进入一个月倒计时,4 月第一周才能拿到离职证明。
最后在这里祝各位小伙伴能找到称心如意的工作~
三 HTML
HTML 属于结构层,负责描绘出内容的结构。
CSS 属于表示层,负责如何显示有关内容。
JavaScript 属于行为层,负责内容应如何对事件做出反应。
3.1 HTML 学习推荐
3.2 HTML 语义化
语义化的含义就是用正确的标签做正确的事情,HTML 语义化就是让页面的内容结构化,它有如下优点:
- 便于对浏览器、搜索引擎解析;
- 便于盲人浏览网页;
- 便于阅读源代码的人对网站进行分开,维护和理解;
简单来说,能用 <header>
、<footer>
等 H5 新标签的就不用 <div class="header">
,不要使用 <div>
来存放段落等……
3.3 HTML5 新标签
HTML5 中新增标签大致有:<header>
、<footer>
、<aside>
、<nav>
、<video>
、<audio>
、<canvas>
等等。
3.4 常见浏览器及其内核
Chrome | Firefox | Safari | IE | Opera | |
---|---|---|---|---|---|
排版引擎 | Blink | Gecko | Webkit | Trident | Blink |
JS 引擎 | V8 | SpiderMonkey | Nitro | Chakra | V8 |
国内一些浏览器使用较多的是 Webkit 内核。
- 针对不同浏览器内核,HTML 辨别:
- IE 内核浏览器识别:
<!--[if IE]><![endif]-->
- 非 IE 内核浏览器识别:
<!--[if !IE]><![endif]-->
- 针对不同浏览器内核,CSS 辨别:
- /* 设置文字不可选取 */
- * {
- -moz-user-select: none; /* 火狐 浏览器 */
- -webkit-user-select: none; /* Webkit 浏览器 */
- -o-user-select: none; /* Opera 浏览器 */
- -ms-user-select: none; /* IE10 浏览器 */
- -khtml-user-select: none; /* 早期浏览器 */
- user-select: none; /* 默认 */
- }
- 复制代码
3.5 cookies、session、sessionStorage、localStorage
-
cookies:存储于浏览器端的数据。可以设置 cookies 的到期时间,如果不设置时间,则在浏览器关闭窗口的时候会消失。
-
session:存储于服务器端的数据。session 存储特定用户会话所需的属性和配置信息。
-
cookies 和 session 的区别在于:
- cookies 数据存放在客户的浏览器上,session 数据存放在服务器上。
- 前端都是裸君子,没有安全可言,cookies 可能会被黑客利用作数据欺骗。所以重要信息记得存 session。
- session 如果在生效期内量过大,会占用服务器性能。
- 单个 cookies 保存的数据不能超过 4 K,很多浏览器限制一个站点保存最多 20 个 cookies。
-
sessionStorage:生命周期存在于标签页或窗口,用于本地存储一个会话(session)中的数据,这些数据会随着窗口或者标签页的关闭而被清空。
-
localStorage:生命周期是永久的,除非用户主动清除浏览器上存储的 localStorage 信息,否则它将会永久存在。
-
sessionStorage 和 localStorage 操作方法:
setItem
、getItem
以及removeItem
。
以 localStorage 为例:
- localStorage.getItem('name'); // 获取 name 的值
- localStorage.setItem('name', 'jsliang'); // 设置 name 的值为 jsliang
- localStorage.removeItem('name'); // 删除 name 的值
- 复制代码
参考 1:《前端分享之cookie的使用及单点登录》
参考 2:《Cookie、session和localStorage、以及sessionStorage之间的区别》
四 CSS
HTML 属于结构层,负责描绘出内容的结构。
CSS 属于表示层,负责如何显示有关内容。
JavaScript 属于行为层,负责内容应如何对事件做出反应。
4.1 CSS 学习推荐
4.2 CSS reset
在工作的过程中,会发现各式各样的浏览器对某个标签有自己独特的样式。
但是在前端开发中,如果不采用统一标准,那么会产生千奇百怪的 bug。所以为了减少后期 bug 的出现,前端开发人员会重置一遍 CSS 样式,尽可能地使开发的网页在各个浏览器相差不大。
下面是 jsliang 在使用的样式重置,当然如果小伙伴有不同的想法,可以去 百度/必应/google 搜索并使用其他版本的样式重置:
4.3 CSS 盒模型
在工作的过程中,也许小伙伴需要 div 块的总宽度为 100px,然后发现总是被 margin 撑高,这是因为盒模型定义的问题:
CSS 中有个属性叫 box-sizing
。
- box-sizing: border-box
- box-sizing: content-box
- 复制代码
border-box
中,整个div
的宽、高,包括margin
、padding
、border
。content-box
中,整个div
的宽、高,则不包括上面元素。
如上图,如果一个 div
,你的代码如下:
- div {
- box-sizing: border-box;
- margin: 10px;
- width: 100px;
- height: 100px;
- padding: 10px;
- }
- 复制代码
那么,你的整个宽高还是 100px
。
但是,如果你的代码如下:
- div {
- box-sizing: content-box;
- margin: 10px;
- width: 100px;
- height: 100px;
- padding: 10px;
- }
- 复制代码
那么,你的整个盒子宽高是 120px
。
如果你在设计页面中,发现内容区被撑爆了,那么,请检查下现在的 border-box
是什么,最好在引用 reset.css
的时候,就对 border-box
进行统一设置,方便管理。
4.4 CSS 单位
在 CSS 中,除了我们常用的 px
,还有其他单位小伙伴们可以了解一下:
单位 | 描述 |
---|---|
% | 百分比 |
px | 像素。计算机屏幕上的一个点为 1px 。 |
em | 相对单位。相对于父元素计算,假如某个 p 元素为 font-size: 12px ,在它内部有个 span 标签,设置 font-size: 2em ,那么,这时候的 span 字体大小为:12 * 2 = 24px |
rem | 相对单位。相对于根元素 html 的 font-size ,假如 html 为 font-size: 12px ,那么,在其当中的 div 设置为 font-size: 2rem ,就是当中的 div 为 24px 。 |
rpx | 微信小程序相对单位。1rpx = 屏幕宽度 / 750 px。在 750px 的设计稿上,1rpx = 1px。 |
除此之外还有 pt、ex 等单位,但由于不太好换算,故在此不提。
4.5 CSS 选择器
选择器是匹配元素的一种模式。
- 关于 CSS 解析器:
HTML 经过解析生成 DOM Tree;而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。
Render Tree 中的元素与 DOM 元素相对应,但非一一对应:一个 DOM 元素可能会对应多个 renderer,如文本折行后,不同的「行」会成为 render tree 种不同的 renderer。也有的 DOM 元素被 Render Tree 完全无视,比如 display:none 的元素。
在建立 Render Tree 时,浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果来确定生成怎样的 renderer。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector。
- CSS 解析顺序
在 CSS 的选择器中,它会按照优先级 从右向左解析,因为这样匹配元素的时候,能尽量少地查找,所以选择器最好写地简洁一点。
- CSS 常用选择器
- 通配符:
*
- ID 选择器:
#ID
- 类选择器:
.class
- 元素选择器:
p
、a
等…… - 后代选择器:
p span
、div a
等…… - 伪类选择器:
a:hover
等…… - 属性选择器:
input[type="text"]
等…… - 子元素选择器:
li:firth-child
、p:nth-child(1)
等……
- CSS 选择器权重
!important -> 行内样式 -> #id -> .class -> 元素和伪元素 -> * -> 继承 -> 默认
4.6 CSS 常见布局
- 水平垂直居中。这种布局老生常谈,jsliang 在本文也有提到,详解请 点击链接
- 两列布局。一侧固定,另一侧自适应。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Two Column Layout</title>
- <style>
- .container {
- display: flex;
- }
- .child-one {
- width: 300px;
- height: 300px;
- background: red;
- }
- .child-two {
- width: 100%;
- height: 300px;
- background: deepskyblue;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="child-one"></div>
- <div class="child-two"></div>
- </div>
- </body>
- </html>
- 复制代码
- 三列布局。类似于两列布局,新增多一个固定宽的
<div>
块而已。当然,小伙伴们可能会说:jsliang 你要考虑flex
的兼容性啊!enm...支持所有最新版本的浏览器!请更新你的浏览器哦亲~
避免被寄刀片,附上
float
布局:《css常见布局》
4.7 CSS3 新特性
经典:CSS3 相关属性你了解吗,说说都有哪些?能说说你工作中常用的一些 CSS3 属性吗?
那么,CSS3 新特性都有哪些呢?
- transition:过渡
- transform:旋转、缩放、移动或者倾斜
- animation:动画
- gradient:渐变
- shadow:阴影
- border-radius:圆角
为了方便记忆,咱将它们扔到同一个 HTML 文件上,小伙伴拷贝到本地上打开,可以看到一个拥有渐变的小球,做着横向运动,如果你鼠标移动到它上面,它的宽度会放大,并且进行倾斜。
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>CSS3 新特性</title>
- <style>
- div {
- width: 100px;
- height: 100px;
- border-radius: 50px;
- background: linear-gradient(red, orange);
- box-shadow: 10px 10px 5px #888888;
- position: relative;
- transition: width 2s;
- animation: mymove 5s infinite;
- }
- div:hover {
- width:300px;
- transform: rotate(7deg);
- }
- @keyframes mymove {
- from { left: 0px; }
- to { left: 200px; }
- }
- </style>
- </head>
- <body>
- <div></div>
- </body>
- </html>
- 复制代码
参考 1:《CSS3 圆角》
参考 2:《CSS3 渐变(Gradients)》
参考 3:《CSS3 transition 属性》
参考 4:《CSS3 transform 属性》
参考 5:《CSS3 animation(动画) 属性》
参考 6:《CSS3 box-shadow 属性》
参考 7:《个人总结(css3新特性)》
4.8 BFC
- 什么是 BFC?
BFC 就是 块级格式上下文,它是一个独立的渲染区域,让处于 BFC 内部的元素和外部的元素相互隔离,使内外元素的定位不会相互影响。
一定的 CSS 声明可以生成 BFC,浏览器对生成的 BFC 有一系列的渲染规则,利用这些渲染规则可以达到一定的布局效果。
- 为什么需要 BFC 呢?
- 它可以防止 margin 元素重叠(div 中包含 ul,而 div 与 ul 之间的垂直距离,取决于 div、ul、li 三者之间的最大外边距,这时候给 ul 一个 display:inline-block 即可解决这个问题)
- 清除内部浮动(div 中包含 ul,而 ul 采用 float:left,那么 div 将变成一长条,这时候给 div 加上规则使其变成 BFC 即可)
- 如何产生 BFC?
- display: inline-block
- position: absolute/fixed
- 工作中一般可能不会顾及这个:
- float 很少使用了,尽可能使用 flex
- css reset 一般会清除掉一些问题,减少 BFC 的使用。
参考文献:《我对BFC的理解》
4.9 行内元素和块级元素
行内元素:宽度和高度由内容决定,与其他元素共占一行的元素,我们将其叫行内元素。例如:<span>
、<i>
、<a>
等……
块级元素:默认宽度由父容器决定,默认高度由内容决定,独占一行并且可以设置宽高的元素,我们将其叫做块级元素。例如:<p>
、<div>
、<ul>
等……
在日常开发中,我们经常使用 CSS 的 display
属性来打破两者的壁垒:display: inline-block
,使它们拥有更多的状态。
4.10 行内样式、内嵌式、链接式以及导入式
在引用 CSS 上,分为四种形式:行内样式、内嵌式、链接式以及导入式,下面介绍这四种模式。
- 行内样式
直接对 HTML 的标记使用 style 属性,然后将 CSS 代码直接写进去:
- <p style="color: #fff; backgournd: deepskyblue;"></p>
- 复制代码
- 内嵌式
将 CSS 写 <head>
与 </head>
之间,并且用 <style>
和 </style>
标记进行声明:
- <head>
- <style>
- p {
- color: #fff;
- background: deepskyblue;
- }
- </style>
- </head>
- 复制代码
- 链接式
通过将 <style>
上的 CSS 提起到指定的 CSS 文件上,然后通过 <link>
的方式在 HTML 上链接起来。
- <head>
- <link href="reset.css" type="text/css" rel="stylesheet">
- </head>
- 复制代码
- 导入样式
- <head>
- <style>
- @import url(reset.css);
- </style>
- </head>
- 复制代码
- 各种方式的优先级
在优先级上,行内样式 > 链接式 > 内嵌式 > @import 导入式。
4.11 水平垂直居中
- 什么是 Flex 布局?
Flex 是 Flexible Box 的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
- Flex 布局有哪些便利
- /* 设置 Flex 模式 */
- display: flex;
-
- /* 决定元素是横排还是竖着排,要不要倒序 */
- flex-direction: column;
-
- /* 决定元素换行格式,一行排不下的时候如何排 */
- flex-wrap: wrap;
-
- /* flex-flow = flex-direction + flex-wrap */
- flex-flow: column wrap;
-
- /* 同一排下对齐方式,空格如何隔开各个元素 */
- justify-content: space-between;
-
- /* 同一排下元素如何对齐,顶部对齐、中部对齐还是其他 */
- align-items: center;
-
- /* 多行对齐方式 */
- align-content: space-between;
- 复制代码
- 如何通过 Flex 实现元素水平垂直居中?
HTML
- <div class="container">
- <div class="child"></div>
- </div>
- 复制代码
CSS
- .container {
- margin: 0 auto;
- width: 300px;
- height: 200px;
- background: deepskyblue;
- display: flex;
- /* 实现元素水平居中 */
- justify-content: center;
- /* 实现元素垂直居中 */
- align-items: center;
- }
- .child {
- width: 100px;
- height: 100px;
- background: #fff;
- }
- 复制代码
- 除了 Flex,还能使用其他形式进行水平垂直居中吗?
HTML
- <div class="container">
- <div class="child"></div>
- </div>
- 复制代码
CSS
- .container {
- position: relative;
- width: 300px;
- height: 200px;
- background: pink;
- margin: 0 auto;
- }
- .child {
- position: absolute;
- width: 100px;
- height: 100px;
- top: 50%;
- left: 50%;
- /* 下面两种方式均可 */
- /* margin-top: -50px;
- margin-left: -50px; */
- transform: translate(-50%, -50%);
- background: #fff;
- }
- 复制代码
- 除此之外再谈谈 CSS 水平居中或者垂直居中?
水平居中:
- 行内元素:
display: inline-block; text-align: center;
- 块级元素:
margin: 0 auto;
- Flex:
display: flex; justify-content: center;
垂直居中:
- 行高 = 元素高:
line-height: height
- Flex:
display: flex; align-items: center;
参考文献:
① 《CSS实现垂直居中的常用方法》
② 《CSS 用 position: absolute 与 transform 来居中块级元素的问题》
五 JavaScript
HTML 属于结构层,负责描绘出内容的结构。
CSS 属于表示层,负责如何显示有关内容。
JavaScript 属于行为层,负责内容应如何对事件做出反应。
5.1 JS 学习推荐
- 《JavaScript 高级程序(第三版)》
- 《你不知道的 JavaScript》
- 《JavaScript 忍者秘籍》
- 《ES6 标准入门》—— 阮一峰
- 《JavaScript 设计模式》—— 张容铭
- 《JavaScript 设计模式与开发实践》—— 曾探
5.2 JS 引用方式
- 行内引入:
- <body>
- <input type="button" onclick="alert('行内引入')" value="按钮"/>
- <button onclick="alert(123)">点击我</button>
- </body>
- 复制代码
- 内部引入:
- <script>
- window.onload = function() {
- alert("js 内部引入!");
- }
- </script>
- 复制代码
- 外部引入:
- <body>
- <div></div>
-
- <script type="text/javascript" src="./js/index.js"></script>
- </body>
- 复制代码
注意:
- 不推荐写行内或者在 HTML 中插入
<script>
,因为浏览器解析顺序缘故,如果解析到死循环之类的 JS 代码,会卡住页面。 - 建议在 onload 事件之后,即等 HTML、CSS 渲染完毕再执行代码。
5.3 原型与原型链
关于 prototype
、__proto__
、new
、call()
、apply()
、bind()
、this
这些的知识点,由于篇幅太长,jsliang 已经抽离了出来,并做了简洁详细讲解,详见:
下面放出相关知识点:
- 实例的
__proto__
属性(原型)等于其构造函数的prototype
属性。 - Object.proto === Function.prototype
- Function.prototype.proto === Object.prototype
- Object.prototype.proto === null
5.4 作用域与闭包
在 JS 中,最容易混淆的就是作用域的情况。
在传统的后端语言(例如 C 语言)中,一对花括号 {}
就是一个块级作用域,作用域内变量不会相互影响,但是在 JS 中,像 if 条件语句的 {}
就不算一个独立的作用域:
- var x = 1;
- console.log(x); // 1
- if(true) {
- var x = 2;
- console.log(x); // 2
- }
- console.log(x); // 2
- 复制代码
所以有时候我们就需要变通,通过自执行函数创建临时作用域:
- function foo() {
- var x = 1;
- console.log(x); // 1
- if(x) {
- (function(x) {
- console.log(x); // 1
- var x = 2;
- console.log(x); // 2
- })(x)
- }
- console.log(x); // 1
- }
- foo();
- 复制代码
说到创建临时作用域,我们就不得不谈一下闭包。
那么,什么是闭包呢?
闭包简单定义:函数 A 里面包含了 函数 B,而 函数 B 里面使用了 函数 A 的变量,那么 函数 B 被称为闭包。
又或者:闭包就是能够读取其他函数内部变量的函数
- function A() {
- var a = 1;
- function B() {
- console.log(a);
- }
- return B();
- }
- 复制代码
- 闭包经典问题:现在我们有一段代码:
- for(var i = 0; i < 3; i++) {
- setTimeout(function() {
- console.log(i);
- }, 1000);
- }
- 复制代码
请问这段代码输出什么?
答案:3 个 3。
解析:首先,for
循环是同步代码,先执行三遍for
,i 变成了 3;然后,再执行异步代码setTimeout
,这时候输出的 i,只能是 3 个 3 了。
- 那么,我们有什么办法依次输出 0 1 2 么?
- 使用 let:
- for(let i = 0; i < 3; i++) {
- setTimeout(function() {
- console.log(i);
- }, 1000);
- }
- 复制代码
在这里,每个 let 和代码块结合起来形成块级作用域,当 setTimeout() 打印时,会寻找最近的块级作用域中的 i,所以依次打印出 0 1 2。
如果这样讲不明白,我们可以执行下下面这段代码:
- for(let i = 0; i < 3; i++) {
- console.log("定时器外部:" + i);
- setTimeout(function() {
- console.log(i);
- }, 1000);
- }
- 复制代码
此时浏览器依次输出的是:
- 定时器外部:0
- 定时器外部:1
- 定时器外部:2
- 0
- 1
- 2
- 复制代码
即代码还是先执行 for
循环,但是当 for
结束执行到了 setTimeout
的时候,它会做个标记,这样到了 console.log(i)
中,i 就能找到这个块中最近的变量定义。
- 使用立即执行函数解决闭包问题
- for(let i = 0; i < 3; i++) {
- (function(i){
- setTimeout(function() {
- console.log(i);
- }, 1000);
- })(i)
- }
- 复制代码
以上,我们就讲解完了闭包及解决闭包的方式。
观点 1:有些资料表示闭包中产生的大量局部变量,会造成内存消耗过大,从而造成网页的性能问题。
观点 2:有些资料表示目前浏览器引擎都基于 V8,而 V8 引擎有个 gc 回收机制,不用太过担心变量不会被回收。
提示:所以,如果你觉得不够保险,那就在退出函数之前,将不使用的局部变量全部删除。
5.5 浅拷贝与深拷贝
- 什么是深拷贝?什么是浅拷贝?
简单来说,有两个对象 A 和 B,B = A,当你修改 A 时,B 的值也跟着发生了变化,这时候就叫浅拷贝。如果不发生变化,就叫深拷贝。
- 为什么会出现深拷贝与浅拷贝?
- 首先我们需要知道基本数据类型(number、string、boolean、null、undefined)与引用数据类型(无序对象,数据以及函数)。
- 然后在基本数据类型中,例如:
let a = 1; let b = a; a = 2; console.log(b)
。当我们尝试这样子写时,b 在栈内存中开辟了一个新内存,所以 b 的值不会改变,仍是 1. - 接着在引用数据类型中,例如
let a = [1, 2, 3], b = a; a[0] = 3; console.log(b)
。当我们尝试这样子写时,b 会偷懒,引用跟 a 同一块的内存地址,从而 a 的修改会影响 b,使得 b 变成 [3, 1, 3]。 - 最后,我们可以知道在引用数据类型中,会产生浅拷贝的问题。
- 如何实现深拷贝?
- 首先我们尝试使用递归去解决深拷贝:
- function deepClone(obj) {
- let objClone = Array.isArray(obj) ? [] : {};
- if(obj && typeof obj === "object") {
- for(key in obj) {
- if(obj.hasOwnProperty(key)) {
- // 判断 obj 子元素是否为对象,如果是,递归复制
- if(obj[key] && typeof obj[key] === "object") {
- objClone[key] = deepClone(obj[key]);
- } else {
- // 如果不是,简单复制
- objClone[key] = obj[key];
- }
- }
- }
- }
- return objClone;
- }
-
- let a = [1, 2, 3, 4];
- let b = deepClone(a);
- a[0] = 2;
- console.log(a, b);
-
- // Console
- // a = [2, 2, 3, 4];
- // b = [1, 2, 3, 4];
- 复制代码
- 使用 JSON 对象的 parse 和 stringify
注意:采用 JSON 进行的深拷贝,无法拷贝到 undefined、function、symbol 这类数据,它是有小 bug 的深拷贝。
- function deepClone(obj) {
- let _obj = JSON.stringify(obj);
- let objClone = JSON.parse(_obj);
- return objClone
- }
- let a = [0, 1, [2, 3], 4];
- let b = deepClone(a);
- a[0] = 1;
- a[2][0] = 1;
- console.log(a, b);
-
- // Console
- // a = [1, 1, [1, 3], 4];
- // b = [0, 1, [2, 3], 4];
- 复制代码
5.6 模块化与组件化
在前端发展中,随着前后端分离,前端社区的不断壮大,前端能做的事情越来越多,承受的任务越来越重,代码也就越来越长了。就好比 jsliang 个人使用 jQuery 开发的时候,动不动就上千行代码,这在一个编辑器上看起来就有点乱了。如果碰上没有代码折叠的编辑器,你就更加难受了。
有的小伙伴的编辑器不是 VS Code,也不能进行代码折叠
所以,面对越来越多的代码,我们就急需将这些代码分门别类,将代码按功能划分,将同一功能的代码整合在一起,于是就有了模块化开发:一个文件就是一个模块,当我们需要某个文件的时候,我们只需要引用这个模块即可……
首先,是 CommonJS 的提出,在 Node.js 以及 Webpack 都支持 CommonJS,它规定了一个文件就是一个模块,文件内部定义的变量属于这个模块,不会对外暴露从而污染全局变量的规则。在 CommonJS 中,通过 exports 或者 module.exports 进行导出,通过 require 进行 同步加载 所需要依赖的模块。由于它是同步加载模块的形式,所以比较通用于服务器端。
然后,根据 CommonJS 只能同步加载的问题,AMD 根据浏览器的特性,进行了非同步加载模块的提出。同时,AMD 有个问题,就是在使用 require.js 的时候,必须提前加载所有模块。
接着,根据 AMD 的问题,CMD 提出来了:通过按需加载的形式,哪里需要就调用哪里,而不用等到所有的模块都加载了再解析。
最后,ECMA 国际推出了 ES6 的 modules。在 ES6 中,通过 export 关键字导出模块,通过 import 关键字引用代码。当然,由于浏览器厂商诸多,ES6 在浏览器的尚不支持,目前主流做法是先将 ES6 通过 babel 编译成 require。
当然,JS 都进行模块化了,jsliang 想起自己项目中的那一坨 CSS,真心没有回顾的想法!所以我们还需要知道为了方便管理 CSS,大佬们还是有做事儿的:Less 以及 Sass,这两者使 CSS 的编写更有组织性和目的性了。
说起模块化,我们又可以顺带提及组件化了,一开始为了区分这两者,jsliang 也是百度了大量文章,最后成功把自己整蒙了,还是说说感觉可以的解释:
组件化更关注的是 UI 部分:弹出框、头部,内容区、按钮等,都可以编写成组件,然后在适用的地方进行引用。而模块化更侧重于功能或者数据的封装,比如全局的 JSON 配置文件,比如通用的验证方法,比如规范时间戳等。
所以,说到这里,我们就可以提到前端工程化:将整个开发流程就行工程规划,从而提高整个团队的开发效率。
在前端工程化中,最重要的就是提高整个团队在 编码 -> 测试 -> 维护 这三个阶段的生产效率。团队的协调至关重要,将每个任务细分给各个成员,从而获取极致的工作效率,是管理者最喜欢看到的。而在上面的模块化和组件化的应用,就属于前端工程化中的一部分,其目的就是在一些复杂的项目中,方便团队进行合作开发,提高生产效率。
参考文献:
① 《到底什么是前端工程化、模块化、组件化》
② 《【前端工程化系列】简谈前端模块化开发与开发规范》
③ 《个人关于模块化的理解》
④ 《组件化开发和模块化开发概念辨析》
⑤ 《JavaScript模块化 --- Commonjs、AMD、CMD、es6 modules》
⑥ 《浅谈什么是前端工程化》
5.7 面向对象与面向过程
- 什么是面向过程与面向对象?
- 面向过程就是做围墙的时候,由你本身操作,叠第一层的时候:放砖头,糊水泥,放砖头,糊水泥;然后第二层的时候,继续放砖头,糊水泥,放砖头,糊水泥……
- 面向对象就是做围墙的时候,由他人帮你完成,将做第一层的做法抽取出来,就是放砖头是第一个动作,糊水泥是第二个动作,然后给这两个动作加上步数,最后告诉机器人有 n 层,交给机器人帮你工作就行了。
- 为什么需要面向对象写法?
- 更方便
- 可以复用,减少代码冗余度
- 高内聚低耦合
简单来说,就是增加代码的可复用性,减少咱们的工作,使代码更加流畅。
- 手写个面向对象代码?
- function Person(name, phone) {
- this.name = name;
- this.phone = phone;
- this.eat = function() {
- console.log(name + " 吃饭");
- }
-
- return this;
- }
-
- let p1 = new Person("jsliang", "18818881888");
- console.log(p1.name); // jsliang
- p1.eat(); // jsliang 吃饭
- 复制代码
当然,jsliang 只能写到这里了,再写下去就是设计模式等知识点了。
所以希望小伙伴们还是了解下面向对象思想,有助于进一步提升自己。
5.8 防抖与节流
关于 防抖与节流,jsliang 特意将资料结合起来:
- 防抖与节流
- 重绘与回流
- 浏览器解析 URL
- DNS 域名解析
- TCP 三次握手与四次挥手
- 浏览器渲染页面
小伙伴们可以前往 《面试知识点 - JS 防抖与节流》 查看。
5.9 ES6
ES6 是个大知识点,如果你面试的公司不是 “饱经沧桑” 的那种,那么一定会出点 ES6 问题,例如:
- 说说 let、var、const 区别
- 讲讲 Promise 及其使用
因为 jsliang 感觉自己连 ES6 的门还没进,所以在这里就不 自作聪明,推荐下阮一峰大佬的教程:
希望小伙伴们看完能有所收获,并在工作中大量使用。
5.10 数组操作
在 JavaScript 中,用得较多的之一无疑是数组操作,这里过一遍数组的一些用法:
map
: 遍历数组,返回回调返回值组成的新数组forEach
: 无法break,可以用try/catch中throw new Error来停止filter
: 过滤some
: 有一项返回true,则整体为trueevery
: 有一项返回false,则整体为falsejoin
: 通过指定连接符生成字符串push / pop
: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】unshift / shift
: 头部推入和弹出,改变原数组,返回操作项【有误】sort(fn) / reverse
: 排序与反转,改变原数组concat
: 连接数组,不影响原数组, 浅拷贝slice(start, end)
: 返回截断后的新数组,不改变原数组splice(start, number, value...)
: 返回删除元素组成的数组,value 为插入项,改变原数组indexOf / lastIndexOf(value, fromIndex)
: 查找数组项,返回对应的下标reduce / reduceRight(fn(prev, cur), defaultPrev)
: 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)
相信小伙伴在工作中耍的已经是一套一套的了,或者像 jsliang 一样只会简单的使用 push
、map
这几个,感兴趣的小伙伴可以 百度/bing/google 找找一些 奇技淫巧,说不定对工作效率有很大提升~
六 Vue
推荐:
6.1 MVVM
在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互时双向的,因此 View 数据会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需要关注业务逻辑,不需要手动操作 DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
- M - Model。Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑。
- V - View。View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来。
- VM - ViewModel。ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View。
6.2 生命周期
- 请大致讲下 Vue 的生命周期?
- 创建前/后:在 beforeCreated 阶段,Vue 实例的挂载元素
$el
和数据对象 data 以及事件还未初始化。在 created 阶段,Vue 实例的数据对象 data 以及方法的运算有了,$el
还没有。 - 载入前/后:在 beforeMount 阶段,
render
函数首次被调用,Vue 实例的 $el 和 data 都初始化了,但还是挂载在虚拟的 DOM 节点上。在 mounted 阶段,Vue 实例挂载到实际的 DOM 操作完成,一般在该过程进行 Ajax 交互。 - 更新前/后:在数据更新之前调用,即发生在虚拟 DOM 重新渲染和打补丁之前,调用 beforeUpdate。在虚拟 DOM 重新渲染和打补丁之后,会触发 updated 方法。
- 销毁前/后:在执行实例销毁之前调用 beforeDestory,此时实例仍然可以调用。在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 Vue 实例已经解除了事件监听以及和 DOM 的绑定,但是 DOM 结构依然存在。
- 什么是 Vue 生命周期?
Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载 DOM -> 渲染、更新 -> 渲染、销毁等一系列过程,称之为 Vue 的生命周期。
- Vue 有几个生命周期,它们的作用主要是什么?
8 个,创建前/创建后、挂载前/挂载后、更新前/更新后、销毁前/销毁后。Vue 生命周期的作用是方便我们通过它的生命周期,在业务代码中更好地操作数据,实现相关功能。
- 第一次页面加载会触发 Vue 哪几个钩子?
会触发 4 个生命钩子:创建前/创建后、挂载前/挂载后
- DOM 渲染在哪个周期就已经完成?
在 beforeMounted
时它执行了 render
函数,对 $el 和 data 进行了初始化,但此时还是挂载到虚拟的 DOM 节点,然后它在 mounted
时就完成了 DOM 渲染,这时候我们一般还进行 Ajax 交互。
6.3 双向数据绑定
Vue 采用 数据劫持 结合 发布者-订阅者 模式的方式,通过 Object.defineProperty()
来劫持各个属性的 setter 以及 getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
- 第一步:需要 Observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
- 第二步:Compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新数据。
- 第三步:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情有:
- 在自身实例化时往属性订阅器(dep)里面添加自己。
- 自身必须有一个 update() 方法
- 待属性变动
dep.notice()
通知时,能调用自身的update()
方法,并触发 Compile 中绑定的回调,则功成身退。
- 第四步:MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。
js 实现简单的双向绑定
- <body>
- <div id="app">
- <input type="text" id="txt">
- <p id="show"></p>
- </div>
-
- <script>
- window.onload = function() {
- let obj = {};
- Object.defineProperty(obj, "txt", {
- get: function() {
- return obj;
- },
- set: function(newValue) {
- document.getElementById("txt").value = newValue;
- document.getElementById("show").innerHTML = newValue;
- }
- })
- document.addEventListener("keyup", function(e) {
- obj.txt = e.target.value;
- })
- }
- </script>
- </body>
- 复制代码
Object.defineProperty 接收三个参数:对象,属性名,配置对象
这里使用的是 Object.defineProperty,这是 Vue 2.0 进行双向数据绑定的写法。在 Vue 3.0 中,它使用 Proxy 进行数据劫持。
- 为什么 Vue 3.0 中使用 Proxy 了?
- Vue 中使用 Object.defineProperty 进行双向数据绑定时,告知使用者是可以监听数组的,但是只是监听了数组的 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 这八种方法,其他数组的属性检测不到。
- Object.defineProperty 只能劫持对象的属性,因此对每个对象的属性进行遍历时,如果属性值也是对象需要深度遍历,那么就比较麻烦了,所以在比较 Proxy 能完整劫持对象的对比下,选择 Proxy。
- 为什么 Proxy 在 Vue 2.0 编写的时候出来了,尤大却没有用上去?因为当时 es6 环境不够成熟,兼容性不好,尤其是这个属性无法用 polyfill 来兼容。(polyfill 是一个 js 库,专门用来处理 js 的兼容性问题-js 修补器)
6.4 Virtual DOM
Vue 在 render
中 createElement
的时候,并不是产生真实的 DOM 元素,实际上 createElement
描述为 createNodeDescription
,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点。
因此,我们将这样的节点描述为 “虚拟节点”(Virtual Node),简称 VNode。“虚拟 DOM” 是我们对由 Vue 组件树建立的整个 VNode 树的称呼。
作为一枚切图仔,很荣幸地跟小伙伴说:“其实我也不懂 Virtual DOM!”
但是,总会有些面试场合会提到的,所以这里找了几篇资料,小伙伴们可以进一步学习:
其他的就需要小伙伴自己寻找了,如果觉得有不错的解析 Virtual DOM 的文档/视频,小伙伴也可以推荐过来哈~
6.5 template 编译
- Vue template 编译的理解
Vue 中 template 就是先转化成 AST 树,再得到 render 函数返回 VNode(Vue 的虚拟 DOM 节点)。
- 通过 compile 编译器把 template 编译成 AST 语法树(abstract syntax tree - 源代码的抽象语法结构的树状表现形式),compile 是 createCompiler 的返回值,createCompiler 是用以创建编译器的。另外 compile 还负责合并 option。
- AST 会经过 generate(将 AST 语法树转换成 render function 字符串的过程)得到 render 函数,render 的返回值是 VNode,VNode 是 Vue 的虚拟 DOM 节点,里面有标签名、子节点、文本等待。
6.6 key
key 的作用就是在更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。
对于 diff 过程来说 key 是起不到提速作用的,详见:key 的作用
6.7 nextTick
-
用法:
Vue.nextTick( [callback, context] )
-
参数:
{Function} [callback]
{Object} [context]
-
说明:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
-
案例:
- // 修改数据
- vm.msg = 'Hello'
- // DOM 还没有更新
- Vue.nextTick(function () {
- // DOM 更新了
- })
-
- // 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
- Vue.nextTick().then(function () {
- // DOM 更新了
- })
- 复制代码
关于 nextTick 的更多理解,jsliang 就不献丑了,需要学习的小伙伴可以查看:
或者自行查找更优秀的资源。
6.8 父子组件通讯
关于 Vue 中的父子组件通讯,相信经常开发 Vue 的小伙伴比 jsliang 知道的多很多。
没怎么使用 Vue 的小伙伴可以看下下面的文章,并尝试自己写一写:
下面咱讲下使用 bus.js 实现非父子组件通讯:
假设在工作中,有三个 .vue 文件:A.vue、B.vue、C.vue。A.vue 是主页面,B.vue 和 C.vue 类似于头部导航条和底部导航栏。现在,B.vue 点击会切换路由,C.vue 需要获取 B.vue 传递的信息。
A.vue
- <template>
- <div>
- <top-nav></top-nav>
- <div class="container">
- <router-view></router-view>
- </div>
- <bottom-nav></bottom-nav>
- </div>
- </template>
- 复制代码
bus.js
- import Vue from 'vue';
-
- // 使用 Event Bus
- const bus = new Vue();
-
- export default bus;
- 复制代码
B.vue
- <template>
- <div class="bottom-nav">
- <div class="nav-one" @click="goToPage({path: '/HomeIndex', meta:'首页'})">
- <i class="icon-home"></i>
- <span>首页</span>
- </div>
- </div>
- </template>
-
- <script>
- import bus from '../utils/bus'
- export default {
- methods: {
- goToPage(route) {
- this.$router.push(route.path);
- bus.$emit('meta', route.meta);
- }
- }
- }
- </script>
- 复制代码
C.vue
- <template>
- <div class="top-nav">
- <span class="title">{{title}}</span>
- </div>
- </template>
-
- <script>
- import bus from '../utils/bus'
- export default {
- data() {
- return {
- title: ''
- }
- },
- created() {
- bus.$on('meta', msg=> {
- this.title = msg;
- })
- }
- }
- </script>
- 复制代码
七 微信小程序
微信小程序,简称小程序,英文名 Mini Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。
7.1 文件主要目录及文件作用
- - component —————————————————— 组件文件夹
- - navBar —— 底部组件
- - navBar.js —— 底部组件的 JS 代码
- - navBar.json —— 底部组件的配置文件
- - navBar.wxml —— 底部组件的 HTML 代码
- - navBar.wxss —— 底部组件的 CSS 代码
- - pages ————————————————————— 页面文件夹
- - index —— 首页
- - index.js —— 首页的 JS 代码
- - index.json —— 首页的配置文件
- - index.wxml —— 首页的 HTML 代码
- - index.wxss —— 首页的 CSS 代码
- - public ————————————————————— 图片文件夹
- - utils —————————————————————— 工具文件夹
- - api.js —— 控制 API 的文件
- - md5.js —— 工具 - MD5 加密文件
- - timestamp.js —— 工具 - 时间戳文件
- - app.json ——————————————————— 设置全局的基础数据等
- - app.wxss ——————————————————— 公共样式,可通过 import 导入更多
- - project.config.json ———————— 项目配置文件
- 复制代码
7.2 微信小程序生命周期
onLoad()
:页面加载时触发。onShow()
:页面显示/切入前台时触发。onReady()
:页面初次渲染完成时触发。onHide()
:页面隐藏/切入后台时触发。onUnload()
:页面卸载时触发。
7.3 如何封装数据请求
- 封装接口:
项目/utils/api.js
- // 将请求进行 Promise 封装
- const fetch = ({url, data}) => {
-
- // 打印接口请求的信息
- console.log(`【step 1】API 接口:${url}`);
- console.log("【step 2】data 传参:");
- console.log(data);
-
- // 返回 Promise
- return new Promise((resolve, reject) => {
- wx.request({
- url: getApp().globalData.api + url,
- data: data,
- success: res => {
-
- // 成功时的处理
- if (res.data.code == 0) {
- console.log("【step 3】请求成功:");
- console.log(res.data);
- return resolve(res.data);
- } else {
- wx.showModal({
- title: '请求失败',
- content: res.data.message,
- showCancel: false
- });
- }
-
- },
- fail: err => {
- // 失败时的处理
- console.log(err);
- return reject(err);
- }
- })
- })
-
- }
-
- /**
- * code 换取 openId
- * @data {
- * jsCode - wx.login() 返回的 code
- * }
- */
- export const wxLogin = data => {
- return fetch({
- url: "tbcUser/getWechatOpenId",
- data: data
- })
- }
- 复制代码
- 调用接口:
项目/pages/login/login.js
- import {
- wxLogin,
- } from '../../utils/api.js'
- 复制代码
- 使用接口:
项目/pages/login/login.js
- wxLogin({
- jsCode: this.data.code
- }).then(
- res => {
- console.log("【step 4】返回成功处理:");
- console.log(res.data);
- },
- err => {
- console.log("【step 4】返回失败处理:");
- console.log(err);
- }
- )
- 复制代码
7.4 页面数据传递
- 通过 url 携带参数,在
onLoad()
中通过options
获取 url 上的参数:
代码演示
- <navigator url="../index/index?userId={{userId}}"></navigator>
-
- <!-- 这两段是分别在 HTML 和 JS 中的代码 -->
-
- onLoad: function(options) {
- console.log(options.userId);
- }
- 复制代码
- 通过 Storage 来传递参数:
- wx.setStorageSync('userId', 'jsliang');
- wx.getStorageSync('userId');
- 复制代码
- WXML 传递数据到 JS
login.wxml
- <text bindtap="clickText" data-labelId="{{userId}}">点击传递数据到 JS</text>
- 复制代码
login.js
- clickText(e) {
- console.log(e.currentTarget.labelid)
- }
- 复制代码
- 组件调用传参
组件接收数据:component-tag-name
- Component({
- properties: {
- // 这里定义了innerText属性,属性值可以在组件使用时指定
- innerText: {
- type: String,
- value: 'default value',
- }
- }
- })
- 复制代码
使用组件的页面定义 json
- {
- "usingComponents": {
- "component-tag-name": "../component/component"
- }
- }
- 复制代码
使用组件的页面 HTML 代码
- <view>
- <!-- 以下是对一个自定义组件的引用 -->
- <component-tag-name inner-text="Some text"></component-tag-name>
- </view>
- 复制代码
- 通过接口调用传递参数
7.5 加载性能优化的方法
- 通过
this.$preload()
预加载用户可能点击的第二个页面。 - 组件化页面,出现两次以上的部分都进行封装成组件。
- 提取共用的 CSS 样式。
- 优化图片:TinyPNG。
7.6 微信小程序与原生 APP、Vue、H5 差异
- 微信小程序优劣势:
优势:
- 无需下载
- 打开速度较快
- 开发成本低于原生 APP
劣势:
- 限制多。页面大小不能超过 1M,不能打开超过 5 个层级的页面。
- 样式单一。小程序内部组件已经成宿,样式不可以修改。
- 推广面窄。跑不出微信,还不能跑入朋友圈。
- 微信小程序 VS 原生 APP
微信小程序有着低开发成本、低获客成本、无需下载的优势。
- 微信小程序 VS H5
- 依赖环境不同。一个能在多种手机浏览器运行。一个只能在微信中的非完整的浏览器。
- 开发成本不同。一个可能在各种浏览器出问题。一个只能在微信中运行。
- 微信小程序 VS Vue
微信小程序看似就是阉割版的 Vue。
7.7 微信小程序原理
- 本质上就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面中进行。
- 架构为数据驱动的模式,UI 和数据分离,所有页面的更新,都需要通过对数据的更改来实现。
- 微信小程序分为两个部分:webview 和 appService。其中 webview 主要用来展示 UI,appServer 用来处理业务逻辑、数据及接口调用。它们在两个进程中进行,通过系统层 JSBridge 实现通信,实现 UI 的渲染、事件的处理。
八 浏览器
8.1 浏览器解析 URL
关于 浏览器解析 URL,jsliang 特意将资料结合起来:
- 防抖与节流
- 重绘与回流
- 浏览器解析 URL
- DNS 域名解析
- TCP 三次握手与四次挥手
- 浏览器渲染页面
小伙伴们可以前往 《面试知识点 - JS 防抖与节流》 查看。
8.2 重绘与回流
关于 重绘与回流,jsliang 特意将资料结合起来:
- 防抖与节流
- 重绘与回流
- 浏览器解析 URL
- DNS 域名解析
- TCP 三次握手与四次挥手
- 浏览器渲染页面
小伙伴们可以前往 《面试知识点 - JS 防抖与节流》 查看。
8.3 数据存储
- 存储于代码中,代码执行完毕释放内存。
- 存储于浏览器中,cookie 用于短期存储用户身份,登录状态等较小的信息;localStorage/sessionStorage 用于长期存储数据,浏览器关闭不影响它们的内存,相比于 cookie,storage 能存储较多;IndexedDB 是浏览器提供的接近于 NoSQL 的数据库,允许存储大量数据。
- 存储于数据库中。
8.4 内存管理与垃圾回收
V8 将内存分为两类:新生代内存空间和老生代内存空间。
- 新生代内存空间:主要用来存放存活时间较短的对象。
- 老生代内存空间:主要用来存放存活时间较长的对象。
这两者通过不同的算法,对内存进行管理操作。
8.5 内存泄漏
-
意外的全局变量:无法被回收。
-
定时器:未被正确关闭,导致所引用的外部变量无法被释放。
-
事件监听:没有正确销毁(低版本浏览器可能出现)。
-
闭包:会导致父级中的变量无法被释放。
-
DOM 引用:DOM 被删除时,内存中的引用未被正确清空。
-
如何查看内存变化情况?
使用 Chrome 的 Timeline(新版本 Performance)进行内存标记,可视化查看内存的变化情况,找出异常点。
九 网络协议
9.1 网络分层
目前网络分层可分为两种:OSI 模型和 TCP/IP 模型。
- OSI 模型
- 应用层(Application)
- 表示层(Presentation)
- 会话层(Session)
- 传输层(Transport)
- 网络层(Network)
- 数据链路层(Data Link)
- 物理层(Physical)
- TCP/IP 模型
- 应用层(Application)
- 传输层(Host-to-Host Transport)
- 互联网层(Internet)
- 网络接口层(Network Interface)
更多详情可以查看下面这篇文章,里面讲得非常详细:
9.2 HTTP/HTTPS
- HTTP:超文本传输协议(HTTP)是用于分布式,协作式和超媒体信息系统的应用协议。它是Web上数据交换的基础,是一种 client-server 协议,也就是说请求通常是由像浏览器这样的接受方发起的。
- HTTPS:HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 它是一个 URI scheme(抽象标识符体系),句法类同 http: 体系。用于安全的HTTP数据传输。https:URL 表明它使用了 HTTP,但 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP 与 TCP 之间)。这个系统的最初研发由网景公司进行,提供了身份验证与加密通讯方法,现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。
9.3 HTTP 状态码
首先,我们大致区分下状态码:
- 1**开头 - 信息提示
- 2**开头 - 请求成功
- 3**开头 - 请求被重定向
- 4**开头 - 请求错误
- 5**开头 - 服务器错误
然后,常见的状态码:
- 200 - 请求成功,Ajax 接受到信息了。
- 400 - 服务器不理解请求,工作中常见于跨域的时候后端给我报 400!
- 403 - 服务器拒绝请求。
- 404 - 请求页面错误。
- 500 - 服务器内部错误,无法完成请求。
最后,小伙伴们如果想要了解更多,还是需要自行查找资料的。
9.4 TCP 三次握手与四次挥手
关于 TCP 三次握手与四次挥手,jsliang 特意将资料结合起来:
- 防抖与节流
- 重绘与回流
- 浏览器解析 URL
- DNS 域名解析
- TCP 三次握手与四次挥手
- 浏览器渲染页面
小伙伴们可以前往 《面试知识点 - JS 防抖与节流》 查看。
十 性能优化
通过优化从而提高页面的加载速度。
10.1 HTML 优化
- 避免 HTML 中书写 CSS 代码,因为这样难以维护。
- 使用 Viewport 加速页面的渲染。
- 使用语义化标签,减少 CSS 代码,增加可读性和 SEO。
- 减少标签的使用,DOM 解析是一个大量遍历的过程,减少不必要的标签,能降低遍历的次数。
- 避免 src、href 等的值为空,因为即时它们为空,浏览器也会发起 HTTP 请求。
- 减少 DNS 查询的次数。
10.2 CSS 优化
- 优化选择器路径:使用
.c {}
而不是.a .b .c {}
。 - 选择器合并:共同的属性内容提起出来,压缩空间和资源开销。
- 精准样式:使用
padding-left: 10px
而不是padding: 0 0 0 10px
。 - 雪碧图:将小的图标合并到一张图中,这样所有的图片只需要请求一次。
- 避免通配符:
.a .b * {}
这样的选择器,根据从右到左的解析顺序在解析过程中遇到通配符* {}
会遍历整个 DOM,性能大大损耗。 - 少用 float:
float
在渲染时计算量比较大,可以使用 flex 布局。 - 为 0 值去单位:增加兼容性。
- 压缩文件大小,减少资源下载负担。
10.3 JavaScript 优化
- 尽可能把
<script>
标签放在body
之后,避免 JS 的执行卡住 DOM 的渲染,最大程度保证页面尽快地展示出来。 - 尽可能合并 JS 代码:提取公共方法,进行面向对象设计等……
- CSS 能做的事情,尽量不用 JS 来做,毕竟 JS 的解析执行比较粗暴,而 CSS 效率更高。
- 尽可能逐条操作 DOM,并预定好 CSs 样式,从而减少 reflow 或者 repaint 的次数。
- 尽可能少地创建 DOM,而是在 HTML 和 CSS 中使用
display: none
来隐藏,按需显示。 - 压缩文件大小,减少资源下载负担。
十一 算法
在算法这块,jsliang 觉得自己还是比较薄弱的,如果小伙伴们跟 jsliang 一样,也想丰富下这方面知识,欢迎一起刷 LeetCode 共同进步:
十二 其他
在 【其他】 这章,原本 jsliang 想谈谈面试中的一些小技巧,例如谈薪;或者讲讲 HR 面需要询问的问题,例如工作时长、加班机制、调薪机制等……
但是,最终看来,jsliang 的经历还是有所欠缺,所经历的面试不够 “盛大”,所以说出的话可能就是 “胡言乱语”、“误导观众”,故在此就不献丑了,如果小伙伴们想知道更多,可以通过 QQ 群:798961601
找到我。
☆ 目前 jsliang 通过 3 天的请假,去了 5 场面试,收获了 3 份 offer。
☆ 如果小伙伴不知道简历该怎么写、面试总是镇静不下来、总感觉面试没谱,可以先找 jsliang 聊聊,我会讲讲个人的面试经历,以及听到的其他小伙伴的经历~
十三 总结
在观看这篇文章的过程中,小伙伴可能会有这些疑问:
- 看完觉得不过瘾啊!你怎么就这么 “短” 啊?
回答:
系列套餐你值得拥有!
- 2019 面试实战 - Round One
- 2019 面试实战 - Round Two
- 2019 面试实战 - Round Three
- 面试知识点 - JS 原型与原型链
- 面试知识点 - JS 防抖与节流
- 面试知识点 - 图片
- 你这杂七杂八的都写了什么呀?看完我晕乎了!
回答:
每个人的学习经历是不同的,所拥有的技术、知识点以及工作经验等都是不同的。
所以 jsliang 的目的是通过这篇文章充实自己的同时,顺带挖掘自己的不足,例如面向对象造轮子、算法问题等让 jsliang 想进一步折腾,并应用到工作中。
因此,小伙伴应该根据自己实际去扩展补充属于自己的知识点。
毕竟了解自己的,只有自己!
- 好像你这里写得也不是很全啊?看完我还是一知半解的!
回答:
每个人的目的都是不同的,不可能一篇文章写完所有知识点,同时有些知识点可能 jsliang 也不感兴趣、或者 jsliang 的层次不够,接触不到。
并且每个面试官都可能有自己的一套面试题,如果 jsliang 能将所有的面试题都写出来,那还需要面试官做啥,大家都像考国家证书一样直接电脑考试吧~(我也期待!!!)
如果小伙伴对文章存有疑问,想快速得到回复。
或者小伙伴对 jsliang 个人的前端文档库感兴趣,也想将自己的前端知识整理出来。
或者小伙伴对文章后续的更新感兴趣,掌握更多的面试技巧。
欢迎加 QQ 群一起探讨:798961601
。
十四 参考文献
本文中的许多内容,也许小伙伴看了会觉得眼熟,因为它们大部分是 jsliang 参考大量文献,再经过刷选整理,最后根据自己理解后的一些阐述。
下面是个人觉得非常优秀的文章。
14.1 关于面试
- 《一位前端 2018 绝地求生记》
- 《中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)》
- 《InterviewMap》
- 《一篇文章搞定前端面试》
- 《微信小程序必知面试题》
- 《微信小程序面试题,附答案》
- 《小程序踩过的那些面试题坑,附答案解决方法》
14.2 关于 HTML
- 《前端工程师手册》
- 《HTML 教程- (HTML5 标准) - 菜鸟教程》
- 《前端分享之cookie的使用及单点登录》
- 《Cookie、session和localStorage、以及sessionStorage之间的区别》
14.3 关于 CSS
- 《前端工程师手册》
- 《CSS 权威指南》
- 《CSS 揭秘》
- 《CSS 世界》
- 《我对BFC的理解》
- 《CSS实现垂直居中的常用方法》
- 《CSS 用 position: absolute 与 transform 来居中块级元素的问题》
- 《css常见布局》
- 《CSS3 圆角》
- 《CSS3 渐变(Gradients)》
- 《CSS3 transition 属性》
- 《CSS3 transform 属性》
- 《CSS3 animation(动画) 属性》
- 《CSS3 box-shadow 属性》
- 《个人总结(css3新特性)》
14.4 关于 JS
- 【推荐】《JavaScript - MDN》
- 《小邵教你玩转ES6》
- 《小邵教你玩转JS面向对象》
- 《实现双向绑定Proxy比defineproperty优劣如何》
- 《Vue 中关于 $emit 的用法》
- 《JavaScript 世界万物诞生记》
- 《js中的new()到底做了些什么??》
- 《MDN Function.prototype.call()》
- 《JavaScript中的call、apply、bind深入理解》
- 《箭头函数 - 廖雪峰》
- 《ECMAScript 6 入门 - 阮一峰》
- 《Vue原理解析之Virtual Dom》
- 《virtual-dom(Vue实现)简析》
- 《Vue.nextTick 的原理和用途》
14.5 关于其他
- 《前端性能优化最佳实践》
- 《到底什么是前端工程化、模块化、组件化》
- 《【前端工程化系列】简谈前端模块化开发与开发规范》
- 《个人关于模块化的理解》
- 《组件化开发和模块化开发概念辨析》
- 《JavaScript模块化 --- Commonjs、AMD、CMD、es6 modules》
- 《浅谈什么是前端工程化》
- 《前端分享之cookie的使用及单点登录》
- 《Cookie、session和localStorage、以及sessionStorage之间的区别》
- 《网络分层TCP/IP 与HTTP》
十五 网友反馈
查看了下掘金评论区,感谢各位大大的反馈,由于本人将于 2019年4月1日 入职,故将一些个人觉得不错的自己没有察觉的知识点记录下来,区分于原文,更为了猴年马月后的下一次跳槽进一步完善。
意思就是,jsliang 这货懒得改原文了,小伙伴们看着这里进行知识点补充
- 闭包定义:
函数 A 里面包含了 函数 B,而 函数 B 里面使用了 函数 A 的变量,函数 B 被 return 了出去,那么 函数 B 被称为闭包。
box-sizing
属性:
当值为 border-box
时,宽度 width = content + padding + border
,包含内边距与边框。
当值为 content-box
时,宽度 width = content
,不包含内边距与边框。
- em:
em
是一个相对的大小,这里的相对于元素父元素的 font-size
。
- Side Project:
Side Project 对应的中文就是副业、业余项目或者小项目。
感兴趣的小伙伴可以去了解一下。
push
与shift
系列:
这里原文已备注是有误的,只是一时没空,没有修改。
- CSS 选择器加载顺序:
原文:!important -> 行内样式 -> #id -> .class -> 元素和伪元素 -> * -> 继承 -> 默认
网友:“应该是最后的优先级最高。”
这里最后的优先级最高应该是指同等级优先级覆盖。浏览器通过 CSSParser 将 CSS 解析成 CSS Rule Tree 的时候,没错的话应该是按照原文中的排序先加载,然后同等级的时候,后面的属性覆盖前面的属性。
- ARIA:
对于 HTML5 的语义化,ARIA 的意思是 Accessible Rich Internet Application,aria-*
的作用就是描述这个 Tag 在可视化的情境中的具体信息。例如:
aria-label
:为组件指定内置的文本标签,用来替代开发者没有使用<label>
标签aria-labelledby
:会读取与此具有相同的id
名的值
详情可参考张鑫旭的 《WAI-ARIA无障碍网页应用属性完全展示》
- sessionStorage 和 localStorage:
文章描述不够详细。
- 两列布局/三列布局:
child-tow
中设置width: 100%
的时候child-one
的宽度会随机而变,设置flex: 1
就不会。所以看个人需求进行设置。- 文章中没有讲到应对兼容性怎么设置,可以考虑使用
float
进行相关的布局。
- 浅拷贝与深拷贝:
可参考文章:《深拷贝的终极探索(90%的人不知道)》
Promise
与async
/await
:
文章描述不够详细。
- 跨域:
本来打算写的,后面没时间,给我删了这块,评论区有篇文献参考:
以上,即为目前评论区的补充,感谢各位小伙伴的点赞支持。
jsliang 广告推送:
也许小伙伴想了解下云服务器
或者小伙伴想买一台云服务器
或者小伙伴需要续费云服务器
欢迎点击 云服务器推广 查看!
jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/LiangJunron…上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。