前端面试 -- JS开发环境和运行环境
JS开发环境和运行环境
在前端面试 – JS-Web-API部分,我们了解到了JS的一些跟DOM和BOM相关的API,但是作为一名专业的前端开发人员,还必须对前端的开发环境和运行环境有一定的了解,这不就巧了吗,本篇文章正是为大家讲述这部分内容的。
开发环境
1-开发环境介绍
1-1 面试怎么考开发环境
- 面试官想通过开发环境了解面试者的经验
- 开发环境,最能体现工作产出的效率
- 会以聊天的形式为主,而不是出具体的问题
1-2 开发环境主要知识点
- IDE(写代码的效率)
- Git(代码版本管理,多人协作开发)
- JS模块化
- 打包工具
- 上线回滚的流程和Linux基础命令
2-IDE
2-1 主流IDE
- webstorm:收费,功能非常强大
- sublime:有免费版,也有收费版,轻量级,页面相对复古一些
- vscode:免费,微软旗下的专门用于前端开发的IDE,轻量级,页面简洁美观
- atom:免费,Github旗下的专门用于前端开发的IDE,轻量级,页面简洁美观
- 插件 插件 插件!!!不管使用哪种开发工具都要安装一些常用的插件来提高自己的编程效率。
2-2 如何选择IDE
- 如果你要走大牛、大咖、逼格的路线,就用 webstorm
- 如果你走普通、屌丝、低调路线,就用 sublime
- 如果你走小清新、个性路线,就用vscode 或者 atom
- 如果你应对面试,最好有一个用得熟悉,其他都会一点
2-3 面试时的注意事项
- 千万不要说你使用 Dreamweaver 或者 notepad++
- 不做 .net 也不要用 Visual Studio
- 不做 java 也不要用 eclipse
3-Git
3-1 为什么要使用Git
- 正式项目都需要代码版本管理
- 大型项目需要多人协作开发
注意: Git 和 Linux 是同一个作者 —— 林纳斯 · 托瓦兹 (Linus Torvalds)
3-2 Git主要知识点
- 网络Git服务器如 coding.net github.com
- 一般公司代码非开源,都有自己的Git服务器
- 搭建Git服务器无需你了解太多
- Git的基本操作必须很熟练
3-3 Git常用命令
- git add . —— 1.开始跟踪新文件 2.把已跟踪的文件放到暂存区 3.合并时把有冲突的文件标记为已解决状态(. 表示对所有新文件进行操作)
- git checkout – xxx —— 放弃修改xxx文件
- git commit -m “xxx” —— 提交 -m是添加注释的命令
- git push [origin master] —— 推送文件到仓库 [指定分支]
- git pull [origin master] —— 拉取仓库文件,用于团队中的其他人更改项目后,可以看到项目最新状态[指定分支]
- git branch —— 查看分支
- git checkout -b xxx —— 新建xxx分支
- git checkout xxx —— 切换到xxx分支
- git merge xxx —— 把xxx分支与当前分支合并
4-JS模块化
这本身就是一个面试的问题
知识点
- 不使用模块化的情况
- 使用模块化的情况
- AMD规范
- CommonJS / ES6模块化
4-1 不使用模块化的情况
- util.js getFormatDate函数
- a-util.js aGetFormatDate函数 使用getFormatDate
- a.js aGetFormatDate
使用// util.js function getFormatDate(date, type) { // type === 1 返回格式 2022-10-16 // type === 2 返回格式 2022年10月16日 // ... } // a-util.js function aGetFormatDate(date) { // 要求返回格式:2022年10月16日 return getFormatDate(date, 2); } // a.js let date = new Date; console.log(aGetFormatDate(date));
<!-- 不使用模块化的情况 --> <script src="util.js"></script> <script src="a-util.js"></script> <script src="a.js"></script> <!-- 1、这些代码中的函数必须是全局变量,才能暴露给使用方。全局变量污染 --> <!-- 2、a.js 知道要引用 a-util.js,但是它知道还需要依赖于 util.js吗? -->
4-2 使用模块化的情况
// util.js
export {
getFormatDate(date, type) {
// type === 1 返回格式 2022-10-16
// type === 2 返回格式 2022年10月16日
// ...
}
}
// a-util.js
let getFormatDate = require('util.js');
export {
aGetFormatDate(date) {
// 要求返回格式:2022年10月16日
return getFormatDate(date, 2);
}
}
// a.js
let aGetFormatDate = require('a-util.js');
let date = new Date;
console.log(aGetFormatDate(date));
// 直接`<script src='a.js'></script>`,其他的根据依赖关系自动引用
// 那两个函数没必要做成全局变量,不会造成污染和覆盖问题
4-3 AMD规范
AMD全称是 Asynchronous Module Definition,即异步模块加载机制。完整描述了模块的定义,依赖关系,引用关系以及加载机制。该规范已被requireJS,nodeJS,Dojo,JQuery使用,可以看出它具有很大的价值。
- require.js requirejs.org/
- 全局 define 函数
- 全局 require 函数
- 依赖JS会自动、异步加载
使用 require.js
注意: 下面代码只作为演示,其中函数功能并未真正实现// util.js define(function() { let util = { getFormatDate: function(date, type) { if(type === 1) { return '2022-10-15'; } if(type === 2) { return '2022年10月15日'; } } } return util; }); // a-util.js define(['./util.js'], function(util) { let aUtil = { aGetFormatDate: function(date) { return util.getFormatDate(date, 2); } } return aUtil; }); // a.js define(['./a-util.js'], function(aUtil) { let a = { printDate: function(date) { console.log(aUtil.aGetFormatDate(date)); } } return a; }); // main.js require(['./a.js'], function(a) { let date = new Date(); a.printDate(date); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>7-1-amd</title> </head> <body> <p>AMD test</p> <script data-main="./main.js" src="https://cdn.bootcss.com/require.js/2.3.3/require.min.js"></script> <!-- <script src="./main.js"></script> --> </body> </html>
4-4 CommonJS / ES6模块化
- CommonJS
- nodejs 模块化规范,现在被大量用于前端,原因:
- 前端开发依赖的插件和库,都可以从 npm 中获取
- 构建工具的高度自动化,使得使用 npm 的成本非常低
- CommonJS不会异步加载JS,而是同步一次性加载出来
- nodejs 模块化规范,现在被大量用于前端,原因:
- CommonJS和ES6模块化的区别
- 因为CommonJS的require语法是同步的,导致CommonJS只适合用于服务端;而ES6模块在浏览器端和服务器端都是可用的,但是在服务端需要遵循特殊的规则
- CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用
- CommonJS模块是运行时加载;ES6模块是编译时输出的接口,方便对JS模块进行静态分析
- 关于互相引用问题,ES6模块中支持加载CommonJS;而CommonJS不支持引用ES6模块
- 参考链接:https://blog.csdn.net/Dax1_/article/details/124081184
- 使用CommonJS
- 需要构建工具支持
- 一般和 npm 一起使用
// util.js modules.exports { getFormatDate(date, type) { if(type === 1) { return '2022-10-15'; } if(type === 2) { return '2022年10月15日'; } } } // a-util.js let util = require('util.js'); modules.exports { aGetFormatDate(date) { // 要求返回格式:2022年10月16日 return util.getFormatDate(date, 2); } }
- AMD 和 CommonJS 的使用场景
- 需要异步加载JS,使用AMD
- 使用了 npm 之后建议用 CommonJS
5-打包工具
知识点
- 安装 nodeJS 和 webpack
- 配置 webpack
- 使用JQuery
5-1 安装 nodeJS 和 webpack
- 安装 nodeJS可参考这篇博客 —— node.js安装及环境配置超详细教程【Windows系统安装包方式】
- 用node启动http服务
- 创建一个带有index.html的目录
- 在该目录下依次输入以下两条命令:
npm i http-server –g // 安装 http-server 依赖 http-server -p 8881 // 在端口8881启动http服务
- 看到以下界面说明http服务启动成功,在浏览器输入http://127.0.0.1:8881/即可看到index.html的页面
注意: 该http服务是轻量级的,只能用于浏览静态页面,不可将其当做nodeJS服务器端使用
- 安装webpack
- 在启动http服务的目录下运行以下两条命令:
npm init // 初始化npm环境 npm i webpack --save-dev // 安装webpack到开发环境
- 初始化npm环境时,相关选项可以参考下面这张图片
- 初始化成功后,可以在当前目录下,看到package.json文件
- webpack安装成功后,在当前目录下可以看到node_modules文件夹,并且package.json文件的具体内容如下:
{ "name": "webpack-test", "version": "1.0.0", "description": "webpack test", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "wmydmu@163.com", "license": "ISC", "devDependencies": { "webpack": "^5.74.0" } }
5-2 配置 webpack
- 在当前目录下创建webpack.config.js文件,并进行配置,具体如下:
var path = require('path') var webpack = require('webpack'); module.exports = { context: path.resolve(__dirname, './src'), // 入口文件路径 entry: { app: './app.js' // 入口文件 }, output: { path: path.resolve(__dirname, './dist'), // 输出文件路径 filename: 'bundle.js' // 输出文件 } }
- 在当前目录下创建src文件夹,并在src目录下,创建app.js文件,在app.js中输入一些语句
- 在package.json文件中的scripts位置加入下面一行命令
"start": "webpack"
- 在当前目录下运行npm start命令,如果运行成功,则会在当前目录下自动生成dist文件夹,并且dist目录下有一个bundle.js文件
- 在index.html中引入bundle.js文件,保存后,重启http服务(可能需要修改端口号),即可在开启的http服务中看到app.js的运行结果
5-3 使用JQuery
- 安装JQuery
在当前目录下运行 npm i jquery --save 命令,即可安装,安装成功后,可以在package.json文件中看到以下内容"dependencies": { "jquery": "^3.6.1" }
- 如果需要卸载依赖,可以运行以下命令:npm uninstall 依赖名
- 在app.js中引入JQuery,并使用,示例如下:
var $ = require('jquery'); console.log($); var $root = $('#root'); $root.html('<p>这是jquery插入的文字</p>');
- 在index.html中创建一个id为root的div,保存后,在当前目录下重新运行npm start命令,重启http服务(可能需要修改端口号),即可在开启的http服务中看到app.js的运行结果,如图所示
6-上线回滚的流程和Linux基础命令
6-1 上线回滚的流程
- 面试
- 不会有具体的问题,以交流询问的方式为主
- 上线回滚流程介绍
- 是非常重要的开发环节
- 各个公司的具体流程不同
- 由专门的工具后台系统完成,我们无需关心细节
- 如果你没有参与过,面试时也要说出要点
- 上线流程要点
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,并生成新的版本号
- 回滚流程要点
- 将当前服务器的代码全部打包并记录版本号,备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号
6-2 Linux基础命令
- 为什么需要学习Linux基础命令
- 服务器使用 Linux 居多,server版,只有命令行
- 测试环境要匹配线上环境,因此也是 Linux
- 经常需要登录测试机来自己配置、获取数据
- Linux基础命令
- ls 列出目录的内容,包括文件和子目录的名称
- ls -l 可以简写为ll,使用详细格式列表,除了文件名之外,还将文件的权限、所有者、文件大小等信息详细列出来。
- mkdir test 在当前路径下创建新文件夹
- pwd 查看当前路径
- vi a.js(空文件) 创建新文件
- rm -rf test 删除test文件夹
- rm a.js 删除a.js文件
- cp a.js a1.js 拷贝当前路径下的a.js到a1.js
- mv a1.js src/a1.js 将当前路径下的a1.js移动到src文件夹
- vim a.js 进入到vim环境,编辑a.js文件(如果没有则新建),点击i可以进行编辑,点击esc退出编辑,无法再输入;保存–1.点击Esc,2输入:w,按回车–保存 3输入:q,按回车–退出(没有修改可以省略第2步,第2步和第3步可以合并为–输入:wq,按回车)
- cat a.js 查看完整的a.js文件
- head a.js 查看a.js头部
- head -n 1 a.js 查看a.js第一行
- tail a.js 查看a.js尾部
- tail -n 2 a.js 查看a.js最后两行a.js
- grep ‘2’ a.js 查看带有‘2’的行
运行环境
1-运行环境介绍
1-1 运行环境介绍
- 浏览器可以通过访问链接来得到页面的内容
- 通过绘制和渲染,显示出页面最终的样子
- 整个过程中,我们需要考虑什么问题?
1-2 运行环境主要知识点
- 页面加载过程
- 性能优化
- 安全性
2-页面加载过程
2-1 题目
- 从输入url到得到html的详细过程
- window.onload 和 DOMContentLoaded 的区别
2-2 知识点
- 加载资源的形式
- 输入 url(或跳转页面)加载 html
- 比如:https://www.baidu.com/
- 加载 html 中的静态资源
- 比如:<script src=“/static/js/jquery.js”></script>
- 输入 url(或跳转页面)加载 html
- 加载一个资源的过程
- 浏览器根据 DNS 服务器得到域名的IP地址
- 向这个IP的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
- 浏览器渲染页面的过程
- 根据 HTML 结构生成 DOM Tree
- 根据 CSS 生成 CSSOM
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到><script> 时,会执行并阻塞渲染(因为JS可以改变 DOM 结构)
- 几个示例
- 示例一
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <p>test</p> </body> </html>
- 示例二
test.css<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" type="text/css" href="test.css"> </head> <body> <div>test</div> </body> </html>
思考:为何要把 css 放在 head 中?div { width: 100%; height: 100px; font-size: 50px; }
这样做可以在加载 DOM 之前拿到样式,渲染 DOM 的时候可以按照样式直接进行渲染。- 示例三
index.js<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <p>test</p> <div id="container">default</div> <script src="index.js"></script> </body> </html>
思考:为何要把 js 放在 body 最下面?document.getElementById('container').innerHTML = 'update by js';
这样做可以在加载 DOM 之后再执行 js 脚本,避免因执行 js 脚本造成的渲染阻塞。- 示例四
window.onload 和 DOMContentLoaded的区别<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <p>test</p> <!-- 假设 test.png 很大,需要很长时间才能加载出来 --> <p><img src="test.png"></p> <p>test</p> </body> </html>
window.addEventListener('load', () => { // 页面的全部资源加载完才会执行,包括图片、视频等 }); document.addEventListener('DOMContentLoaded', () => { // DOM 渲染完即可执行,此时图片、视频还可能没有加载完 });
2-3 题目解答
- 从输入url到得到html的详细过程
- 浏览器根据 DNS 服务器得到域名的IP地址
- 向这个IP的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
- window.onload 和 DOMContentLoaded 的区别
- window.onload:页面的全部资源加载完才会执行,包括图片、视频等
- DOMContentLoaded:DOM 渲染完即可执行,此时图片、视频还可能没有加载完
3-性能优化
3-1 优化策略
- 原则
- 多使用内存、缓存或者其他方法
- 减少 CPU 计算,减少网络请求
- 从哪里入手
- 加载页面和静态资源
- 页面渲染
- 加载资源优化
- 静态资源的压缩合并
- 静态资源缓存
- 使用 CDN 让资源加载更快
- 使用 SSR 后端渲染,数据直接输出到 HTML 中
- 渲染优化
- CSS 放前面,JS 放后面
- 懒加载(图片懒加载、下拉加载更多)
- 减少 DOM 查询,对 DOM 查询做缓存
- 减少 DOM 操作,多个操作尽量合并在一起执行
- 事件节流
- 尽早执行操作(如 DOMContentLoaded)
3-2 几个示例
- 静态资源的压缩合并
<script src="a.js"></script> <script src="b.js"></script> <script src="c.js"></script> <!-- 将以上三个文件合并成一个 --> <script src="abc.js"></script>
- 静态资源缓存
- 通过链接名称控制缓存
- 比如:<script src=“abc_1.js”></script>
- 只有内容改变的时候,链接名称才会改变
- 比如:<script src=“abc_2.js”></script>
- 通过链接名称控制缓存
- 使用 CDN 让资源加载更快
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css"> <script src="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
- 使用 SSR 后端渲染,数据直接输出到 HTML 中
- 现在 Vue React 提出了这样的概念
- 其实 jsp php asp 都属于后端渲染
- 懒加载(图片懒加载、下拉加载更多)
<!-- 先加载比较小的 preview.png, 然后再异步加载真实的、比较大的 abc.png --> <img id="img1" src="preview.png" data-realsrc="abc.png"> <script> let img1 = document.getElementById('img1'); img1.src = img1.getAttribute('data-realsrc'); </script>
- 减少 DOM 查询,对 DOM 查询做缓存
// 未缓存 DOM 查询 for (let i = 0; i < document.getElementsByTagName('p').length; i++) { // todo } // 缓存了 DOM 查询 let pList = document.getElementsByTagName('p'); for(let i = 0; i < pList.length; i++) { // todo }
- 减少 DOM 操作,多个操作尽量合并在一起执行
let listNode = document.getElementById('list'); // 要插入 10个 li 标签 let frag = document.createDocumentFragment(); for(let i = 0; i < 10; i++) { let li = document.createElement('li'); li.innerHTML = "List Item" + i; // frag 不涉及DOM操作 frag.appendChild(li); } listNode.appendChild(frag);
- 事件节流
let textarea = document.getElementById('text'); let timeoutId; textarea.addEventListener('keyup', () => { if(timeoutId) { clearTimeout(timeoutId); } // 如果打字足够快,就不会一直触发 change 事件 timeoutId = setTimeout(() => { // 触发 change 事件 }, 100); });
- 尽早执行操作(如 DOMContentLoaded)
window.addEventListener('load', () => { // 页面的全部资源加载完才会执行,包括图片、视频等 }); document.addEventListener('DOMContentLoaded', () => { // DOM 渲染完即可执行,此时图片、视频还可能没有加载完 });
4-安全性
综合性的问题:应用场景的前端安全问题有哪些
4-1 XSS —— 跨站请求攻击
- XSS 具体表现
- 在新浪博客写一篇文章,同时偷偷插入一段<script>
- 攻击代码中,获取 cookie,发送到自己的服务器
- 发布博客,有人查看博客内容
- 会把查看者的 cookie 发送到攻击者的服务器
- 如何解决 XSS
- 前端替换关键字,例如 < 为 < > 为 >
- 后端替换
4-2 XSRF —— 跨站请求伪造
- XSRF 具体表现
- 你已登录一个购物网站,正在浏览商品
- 该网站付费接口是 xxx.com/pay?id=100 但是没有任何验证
- 然后你收到一封邮件,隐藏着 <img src=“xxx.com/pay?id=100”>
- 你查看邮件的时候,就已经悄悄地付费购买了
- 如何解决 XSRF
- 增加验证流程,如输入指纹、密码、短信验证