前端模块化

随着代码日益增多,模块化开发的 4 个好处:

  1. 避免变量污染,命名冲突
  2. 提高代码复用性
  3. 提高 维护性
  4. 方便管理依赖关系

CommonJS 同步运行时加载的

特点:同步加载(因为 node.js 主要服务端编程文件存在本地硬盘,加载很快),适用于服务端,加载一次缓存其结果。浏览器端需要编译后执行。 使用的是输出值的拷贝,模块内部的变化不会影响到这个值。

每个 JS 文件就是一个模块( module )所以有自己的作用域不会污染全局。私有化。每个模块内部使用 require 函数和 module.exports 对象来对模块进行导入和导出。

commonjs 规范是在运行时加载的,在运行时导出对象,导出的对象与原本模块中的对象是隔离的,简单的说就是深度克隆了一份。

  • 所有模块都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但只会在第一次加载时运行一次。结果被缓存,再运行就必须清除缓存。
  • 加载顺序时按照代码中出现的顺序
  • 输入的是输出值的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值。

AMD 异步模块定义 - 专为浏览器设计

使用 define 进行定义模块,通过 require 进行调用。缺点需要一开始就把所有依赖写出来,也是不太好的。

  1. // moduleA.js
  2. define(function (require) {
  3. var m = require("moduleB");
  4. setTimeout(() => console.log(m), 1000);
  5. });
  6. // moduleB.js
  7. define(function (require) {
  8. var m = new Date().getTime();
  9. return m;
  10. });
  11. // index.js
  12. require(["moduleA", "moduleB"], function (moduleA, moduleB) {
  13. console.log(moduleB);
  14. });

CMD 依赖就近,用的时候再 require

AMD 有个requireJS,CMD 有个浏览器的实现SeaJSSeaJS要解决的问题和requireJS一样,只不过在模块定义方式和依赖模块执行时机上有所不同。

  • AMD 是依赖关系前置,在定义模块的时候就要声明其依赖的模块;
  • CMD 是按需加载依赖就近,只有在使用某个模块的时候再去 require 加载执行;

4.工程化【高级】 - 图1

UMD

同时被 CommonJs 规范和 AMD 规范加载的 UMD 模块,一套同时适用于 node.js 和 web 环境

UMD 先判断是否支持 Node.js 的模块(exports)是否存在,存在则使用 Node.js 模块模式。
在判断是否支持 AMD(define 是否存在),存在则使用 AMD 方式加载模块。

原生 JS 模块化(ES6)

设计思想是尽量的静态化,编译时就能确定模块的依赖关系,以及输入和输出的变量。

与前两者的最大区别在于,ESModule是由 JS 解释器实现,而后两者是 在宿主环境中运行时实现。

ES6 模块与 CommonJS 模块的差异

它们有两个重大差异:

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
  3. CommonJS 是动态语法可以写在判断里,ES5 模块 静态语法只能写在顶层

import() 动态加载模块

import() 返回一个 Promise 对象

  • 按需加载:运行时执行,所以只有运行到这一句才会加载。
  • 条件加载:可以放到 if 代码块中
  • 动态模块路径:允许模块路径动态生成

总结

  • CommonJS 因为是同步加载模块的,所以并不适合在浏览器环境,意味着阻塞加载,浏览器资源适合异步加载所以有了 AMD CMD
  • CMD 和 AMD 规范很类似,但是 AMD 是依赖前置, CMD 是依赖就近。

本地存储

共同点:都是保存在浏览器端,且都遵循同源策略。

cookie 的操作与封装

  • 存储量太小,只有 4KB;
  • 每次 HTTP 请求都会发送到服务端,影响获取资源的效率;
  • 需要自己封装获取、设置、删除 cookie 的方法;

localStorage 和 sessionStorage

  • 限制有 5M , 不同浏览器可能不同。
  • 同源窗口(协议、域名、端口一致),不同页面可以共享 localStorage 值
  • 关于 sessionStorage,通常说 sessionStorage 关闭页面即消失,但是通过跳转的页面可以共享 sessionStorage 值

HTTP、HTTPS、HTTP/2

十分钟搞懂 HTTP 和 HTTPS超详细版本

HTTP 协议(请求与响应、头信息、状态码)

image.png

GET 和 POST 区别

  • GET 用于获取信息,是无副作用的,幂等的,并且可以缓存 url 限制2kb
  • POST 用于修改服务器上的数据,有副作用,非幂等,不可缓存 参数无限制大小
  • 浏览器回退时 GET 是无害的,POST会再次被提交

什么是幂等:多次请求是没有副作用的

HTTP/2

HTTP/2 是 HTTP/1.x 的扩展,而非替代。所以 HTTP 的语义不变,提供的功能不变,HTTP 方法、状态码、URL 和首部字段等这些核心概念也不变。

多路复用 - 巨大的性能提升

在 HTTP/1.1 中,如果客户端想发送多个并行的请求,那么必须使用多个 TCP 连接。
而 HTTP/2 的二进制分帧层突破了这一限制,所有的请求和响应都在同一个 TCP 连接上发送:客户端和服务器把 HTTP 消息分解成多个帧,然后乱序发送,最后在另一端再根据流 ID 重新组合起来。

服务器推送

HTTP/2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。
image.png

HTTPS

http 中存在如下问题:

  • 请求信息明文传输,容易被窃听截取。
  • 数据的完整性未校验,容易被篡改
  • 没有验证对方身份,存在冒充危险

为了解决上述 HTTP 存在的问题,就用到了 HTTPS。
本质上它是 HTTP 的变种,主要解决安全问题,使用了加密传输。加密有三种方式:分别是 证书、对称加密和非对称加密,相比 HTTP 多了一层 SSL/TSL。
1、证书加密:
首先申请该服务器的证书,然后在 HTTPS 的请求过程服务器端会把证书发送给客户端,客户端进行证书验证,以此来验证服务器身份
2、对称加密:
HTTPS 请求中,客户端和服务器之间的通信时通过对称加密算法进行加密的。对称加密,即在加密和解密的过程使用同一个私钥进行加密和解密。

加密过程:加密算法 + 明文 + 私钥 —-》密文
解密过程:解密算法 + 密文 + 私钥 —-》明文
使用场景:对大量数据进行加密时,对称加密是适用的,速度快

3、非对称加密
加密和解密使用了不同的密钥,一个公钥 对外公开、一个私钥 解密使用。由于公钥和私钥是分开的,所以加密算法安全级别高,密文长度有限制,速度较慢。
【 公钥加密、私钥解密】
流程:A 端向 B 端发送请求, B 端把公钥返回,然后 A 使用公钥加密传递加密数据给 B,B 再使用自己的私钥进行解密。
【私钥加密,公钥解密】
流程正好相反

HTTP 和 HTTPS 区别

1、HTTP 明文传输、HTTP 密文传输
2、默认链接的端口号是不同的,HTTP 80 HTTPS 443
3、HTTPS = HTTP + 加密+认证+完整性保护

前后端交互

FormData、upload

  1. var data = new FormData();
  2. data.append("foo", "bar");
  3. data.append("bar", "foo");

ajax 的封装

post 请求发送 form 数据和 json 数据的示例:
设置 request 的 content-type 为 application/x-www-form-urlencoded
设置 request 的 content-type 为 application/json

跨域:jsonp、cors 跨域设置、后端代理

这里有一篇很好的文章讲解链接

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 我们前端通常所说的跨域是狭义的,由同源策略限制引起的

什么是同源策略

是一种约定,是浏览器最核心也是最基本的安全功能,如果缺了同源策略,浏览器很容易受到 XSS、CSFR 等攻击。
同源是指:「协议 + 域名 + 端口」满足三者相同。

限制以下几种行为:

  1. 1.) CookieLocalStorage IndexDB 无法读取
  2. 2.) DOM Js对象无法获得
  3. 3.) AJAX 请求不能发送

解决跨域方案

1、 通过 jsonp 利用 script 标签没有跨域的漏洞,
通过 指向一个需要访问的地址并提供一个回调函数接收数据。缺点:只能实现 get 一种请求。

2、 跨域资源共享(CORS)
普通跨域请求:主要是服务端 header 设置 Access-Control-Allow-Origin 标识哪些域名可以访问资源。
分两种情况:简单请求、复杂请求

  • 简单请求:GET POST HEAD \ content-type: 非 json
  • 复杂请求:除了以上情况,会首先发起一个 option 预检请求,用来检测同福段是否允许跨域请求。

3、 nginx 配置反向代理解决跨域 (利用服务器之间不跨域)
4、 nodejs 中间件代理跨域

vue 框架的跨域:利用 node + webpack + webpack-dev-server(proxy)代理接口跨域

fetch 请求、axios 源码解析

目前阶段会使用、可以自己封装 就 OK 了


H5 核心

文件操作、音频、视频操作

推荐这篇大神写的 链接 TODO: 自己实现一遍最爽

canvas

这里有详细讲解 链接

Echarts、HeighCharts、D3

第三方数据可视化插件


后模块化时代

显然使用 ESModule 的模块明显符合 JS 开发的历史进程,因为任何一个支持 JS 的环境随着对应解释器得分升级,最终一定会支持 ESModule 的标准。但是 并不一定对市面上的浏览器使用新特性,由此 诞生了 大家所熟知的 babel ,能把静态高版本规范代码编译为低版本规范代码,让更多浏览器支持。

但是并不理想,对于 模块化相关的 import 和 export 关键字,babel 最终编译为 require 和 exports 的 CommonJS 规范。导致浏览器中无法运行,为此 我们又做了一步 叫做 打包(bundle)
browserify 和 webpack 都是非常优秀的打包工具,browserify 能够处理 CommonJS 模块化规范的包为 web 直接使用, webpack 则能处理任何模块化规范的内容

Webpack

模块打包机:webpack 是基于入口的。
webpack 会自动地递归解析入口所依赖的所有资源文件,然后用不同的 Loader 来处理不同的文件,用 Plugin 来扩展 webpack 功能。 链接

Webpack 构建流程

  • 1、读取webpack的配置参数;
  • 2、启动webpack,创建Compiler对象并开始解析项目;
  • 3、从入口文件(entry)开始解析,并且找到其导入的依赖模块,递归遍历分析,形成依赖关系树;
  • 4、对不同文件类型的依赖模块文件使用对应的Loader进行编译,最终转为Javascript文件;
  • 5、整个过程中webpack会通过发布订阅模式,向外抛出一些hooks,而webpack的插件即可通过监听这些关键的事件节点,执行插件任务进而达到干预输出结果的目的。

    Loader 加载器

    让 webpack 拥有加载和解析「非 js 文件」的能力

  • css-loader: 加载 CSS,支持模块化、压缩、文件导入等特性
  • image-loader:加载并且压缩图片文件
  • style-loader: 使用 <style> 把 css-loader 生成的内容挂在到 html 页面中
  • file-loader: 把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader: 和 file-loader 类似,小于设置阈(yu)值情况下以 base64 的方式把文件内容注入到代码中 (例如小图)
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
  • babel-loader: 把 ES6、7 等 js 新特性语法转换为 ES5 语法
  • awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader

Plugin 插件

插件扩展 webpack 的功能,在生命周期中广播事件,Plugin 监听事件,在合适的时机 通过 API 改变输出结果。

  • HtmlWebpackPlugin:打包结束后自动生成一个 html 文件,并把打包生成的 js 模块引入到该 html 中。
  • clean-webpack-plugin:打包的时候 清理输出目录文件
  • uglifyjs-webpack-plugin 不支持 ES6 压缩 (Webpack4 以前),如果需要 ES6 代码压缩,请使用
  • terser-webpack-plugin:支持 ES6 压缩,开启 parallel 多线程并发运行以提高构建速度。
  • 主要是删除未引用代码(dead code)的能力,生产环境下默认使用 TerserPlugin
  • webpack-parallel-uglify-plugin: 多核压缩,提高压缩速度
  • webpack-bundle-analyzer: 可视化 webpack 输出文件的体积
  • speed-measure-webpack-plugin:分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。

    1. // 编写自己的插件
    2. class MyPlugin {
    3. apply (compiler) {
    4. // 找到合适的事件钩子,实现自己的插件功能
    5. compiler.hooks.emit.tap('MyPlugin', compilation => {
    6. // compilation: 当前打包构建流程的上下文
    7. console.log(compilation);
    8. // do something...
    9. })
    10. }
    11. }

    用法

  • Loadermodule.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options

  • Pluginplugins中单独配置, 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。

Hot Module Repacement 热更新 HMR(必会

这个机制可以做到不用刷新浏览器,把旧模块更新为新模块。

HMR 的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS (webpack-dev-server)与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。
客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该 chunk 的增量更新。
后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loadervue-loader 都是借助这些 API 实现 HMR。
4.工程化【高级】 - 图4

Babel ES6+ 转 ES5

  • 解析:将代码字符串解析成抽象语法树 AST
    • 词法分析:将代码(字符串)分割为 token 流,即语法单元成的数组
    • 语法分析:分析 token 流(上面生成的数组)并生成 AST
  • 转换:访问 AST 的节点进行变换操作生产新的 AST
  • 生成:以新的 AST 为基础生成代码字符串

文件指纹

文件指纹是打包后输出的文件名的后缀。

  • Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改
  • Chunkhash:和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhash
  • Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变
  1. (module.exports = {
  2. "entry": {
  3. "app": "./scr/app.js",
  4. "search": "./src/search.js"
  5. },
  6. "output": {
  7. "filename": "[name][chunkhash:8].js",
  8. "path": __dirname + "/dist"
  9. }
  10. })

Tree Shaking

移除 js 上下文中的未引用代码(dead-code)依赖于 ES6 模块语法 import export。
通过 package.json 的 “sideEffects” 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯正 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。

开发和生产环境

开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 dev server。

生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。

webpack 有哪些性能优化

  1. 压缩代码、使用 uglifyjs 删除多余的代码、注释、简化代码的写法等等方式
  2. 使用 import() 实现路由懒加载
  3. 使用 babel-plugin-componten 实现 ui 组件按需加载
  4. 利用 CDN 加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径
  5. 利用Tree Shaking删除未使用片段、死代码
  6. 优化图片,对于小图可以使用url-loader base64 的方式写入文件中
  7. 使用 SMP 插件 分析 打包过程中哪些 Loader 和 Plugin 的耗时,方便有针对性完善

如何提高 webpack 的构建速度?

  1. 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
  2. 使用Tree-shaking来剔除多余代码
  3. babel-loader 的 cacheDirectory 选项:将转译的结果缓存到文件系统中,之后的 webpack 构建尝试读取缓存。可以通过 exclude 排除掉一些不需要编译的文件。比如下面就不会去转义 node_modules

Git 操作与管理

post-merge

在多人协作的过程中,我们并不知道别人是否有在 package.json 中更改包管理。如果你需要在 git pull 的时候自动执行 npm install 的话,就可以使用这段代码。
首先在命令行中执行 chmod +x post-merge,让 post-merge 变成可执行文件,然后将这个文件放入 .git/hooks 中,这样下次在执行 git pull 的时候就会自动安装。

常用命令

  • git checkout aaa: 切换 aaa 分支
  • git merge aaa :把 aaa 分支的代码合并到 当前分支
  • 超好用:git stash: 暂存当前修改的文件到, git stash pop: 取出暂存的文件并删除暂存记录,git stash apply: 取出 但是不删除记录
  • git pull 、 git push -u origin aaa
  • git merge --abort将会抛弃合并过程并且尝试重建合并前的状态。但是,当合并开始时如果存在未 commit 的文件,git merge --abort在某些情况下将无法重现合并前的状态。
  • 删除本地分支: git branch -d aaaa
  • 如果删除不了可以强制删除,git branch -D aaaa
  • 有必要的情况下,删除远程分支(慎用):git push origin —delete aaaa

git-flow 工作流 链接

就像代码需要代码规范一样,代码管理同样需要一个清晰的流程和规范
image.png

git-cz 提交规范 链接

目的:统一团队 Git commit 文本标准,便于后续代码 review 和团队协作;


服务

Jenkins 搭建和使用

在 jenkins(一个网站界面)中通过获取代码仓库中最新代码,进行自动化部署,而省去手动打包、上传服务器、部署这一系列步骤,非常方便。

多分支选择配置实战 链接

mock.js 的使用

调试(必备)

postman:调试接口

whistle:基于 Node 实现的 跨平台 web 调试代理工具

Fiddler:抓包工具

这边文章介绍很清楚
汉化版软件下载:
链接: https://pan.baidu.com/s/1f5kQrCzYhRBUP5m7vP1SMg 提取码: 4bp4

移动端调试神器: vconsole