一.HTML&CSS
1-什么是seo优化?
seo就是搜索引擎优化:利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。
-
页面主题优化
实事求是的写下自己网站的名字,网站的名字要合理,最好包含网站的主要内容。
-
页面头部优化
<meta name="keywords" content="" />向搜索引擎说明你的网页的关键词; <meta name="description" content=""/>告诉搜索引擎你的站点的主要内容;
-
超链接优化
不要在页面中出现空链接
按规范书写超链接,这个title属性,它既可以起到提示访客的作用,也可以让搜索引擎知道它要去哪里.
-
图片优化
每个标签加上alt、title属性,alt属性的作用是当图片无法显示时以文字作为替代显示出来,而对于SEO来说,它可以令搜索引擎有机会索引你网站上的图片
-
设置页面中的h1-h6标签
-
优化网站性能,提高网站加载速度
2-页面自适应
自适应是指页面内容自动适应屏幕大小.
实现自适应的方法有多种:
- 1.简易场景实现自适应:浮动、页面居中、元素宽度不写固定而设置百分比自动匹配大小。这样在页面宽度发生变化时,能利用以上特性实现简易的自适应效果。
- 2.如果实际开发中有复杂场景的需求,一般通过编写多套CSS代码,然后用媒体查询技术,让页面根据不同屏幕尺寸来加载不同代码模块以实现适配不同屏幕的目的。这种方式需要编写多套代码,虽然工作量大,但对于不同屏幕尺寸的设备都有单独一套CSS代码,维护起来更方便。
- 3.响应式布局,响应式布局是指根据不同屏幕尺寸自动调整页面显示效果实现自适应(也要用到媒体查询技术)。响应式布局一般有栅格系统布局,flex布局。bootstrap框架的核心就是栅格系统。
自适应网页设计"的核心,就是CSS3引入的Media Query模块。自动探测屏幕宽度,然后加载相应的CSS文件
相关布局方式:
1-媒体查询+rem
- 媒体指的就是各种设备 (移动设备, PC设备)
- 查询指的是要检测属于哪种设备
- 媒体查询: 通过查询当前属于哪种设备, 让网页能够在不同的设备下正常的预览
2-rem + vw(布局)
3-rem布局(等比缩放布局)
4-弹性布局(100%布局、流式布局)
5-圣杯布局
- 圣杯布局是两边固定宽度,中间自适应的三栏布局。 中间栏放到文档流前面,先行渲染。 目的是为了解决三栏布局。
6-双飞翼布局
圣杯布局是中间栏在添加相对定位,并配合left和right属性,效果上表现为三栏是单独分开的(如果可以看到空隙的话),
而双飞翼布局是在中间栏的div中嵌套一个div,内容写在嵌套的div里,然后对嵌套的div设置margin-left和margin-right,效果上表现为左右两栏在中间栏的上面,中间栏还是100%宽度,只不过中间栏的内容通过margin的值显示在中间。
7-grid布局
3-HTML5新特性
(1)HTML5提供了新的元素来创建更好的页面结构:
<article> 表示一块与上下文无关的独立的内容
<aside> 定义页面的侧边栏内容。
<header> 表示页面中一个内容区块或整个页面的头部。
<footer> 表示页面中一个内容区块或整个页面的脚注
<mark> 定义高亮显示的文本(span)
<nav> 定义导航链接的部分。
<section> 定义文档中的节(section、区段)。
<main> 表示页面中的主要的内容(ie不兼容)
<hgroup> 标题的一个分组
<time> 定义日期或时间。
<figure> 表示一段独立的内容,
<figcaption> 为其添加标题(第一个或最后一个子元素的位置)
<dialog> 标记定义一个对话框(会话框)类似微信
<canvas> 画布
(2)HTML5 <canvas>
元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.
(3)HTML5 Audio(音频)、Video(视频) 多媒体标签
<video src=""></video>
<audio src=""></audio>
(4)HTML5 拥有多个新的表单输入类型。这些新特性提供了更好的输入控制和验证。
color、date、datetime、datetime-local、email、month、number、range、search、tel、time、url、week
(5)HTML5 有以下新的表单元素:
<datalist> <input>标签定义选项列表。请与 input 元素配合使用该元素,来定义 input 可能的值。
<output> <output> 标签定义不同类型的输出,比如脚本的输出。
(6) HTML5 的
和 标签添加了几个新属性.4-CSS3新特性
(1)CSS3选择器
- 用CSS3,你可以创建圆角边框,添加阴影框,
(2)CSS3 边框(Borders)
- CSS3中包含几个新的背景属性,提供更大背景元素控制。
(3)CSS3 渐变
- 线性渐变(Linear Gradients)- 向下/向上/向左/向右/对角方向
- 径向渐变(Radial Gradients)- 由它们的中心定义
(4)CSS3 转换和变形
(5)CSS3 过渡和动画
(6)CSS3 多列
(7)CSS3 盒模型
- 在 CSS3 中, 增加了一些新的用户界面特性来调整元素尺寸,框尺寸和外边框,主要包括以下用户界面属性
(8)CSS3伸缩布局盒模型(弹性盒)
- CSS3 弹性盒( Flexible Box 或 flexbox),是一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。
(9)CSS3 媒体查询
媒体指的就是各种设备 (移动设备, PC设备)
查询指的是要检测属于哪种设备
// 媒体查询: 通过查询当前属于哪种设备, 让网页能够在不同的设备下正常的预览
5-CSS中的定位方式(常用)
1.(absolute)绝对定位,参照离自己的最近的,有定位属性的父元素定位。
2.(relantive)相对定位,参照自己本身的位置进行定位。
3.(fixed)固定定位,基于浏览器窗口定位。
4.(static)默认定位,由左到右,从上往下流式布局。
6-如何水平垂直居中一个盒子?(元素水平垂直居中)
body{
/* 第一种:弹性盒 */
display: flex;
justify-content: center;
align-items: center;
}
.box{
width: 200px;
height: 100px;
background: paleturquoise;
/* 第二种:定位 */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
/* 第三种:定位 */
position: absolute;top: 50%;left: 50%;
margin-top: -50px;/* 元素自身高度的一半 */
margin-left: -100px; /* 元素自身宽度的一半 */
/* 第四种:定位 */
position: absolute;
top: 50%;left: 50%;
transform: translate(-50%,-50%);
}
7-图片水平垂直居中(图片大小不一致)
- 1、水平居中: 给img父元素添加text-align:center
- 2、垂直居中: (1)在img前后加一个同级标签span,span转成inline-block,并添加width:0;height:100% (2)span以中线对齐vertical-align:middle (3)img以中线对齐vertical-align:middle
8-盒模型
// 标准盒模型:标准模式下总宽度=width+margin(左右)+padding(左右)border(左右)
// 怪异盒模型:怪异模式下总宽度=width+margin(左右)
9-px、rem、em、vw的区别
- px是相对于显示器屏幕分辨率而言的,固定的一种单位。
- em是相对于父元素的字体大小而言的,譬如父元素字体大小为16px,那么1em=16px,2em=32px。
- rem是相对于根元素(html)字体大小而言的,浏览器默认的字体大小都是16px,那么1rem=16px, 2rem=32px,可以根据设备的宽度,结合媒体查询(@media)来进行自适应的布局。
- vw是窗口的实际宽度,1vw=1% 窗口的实际宽度。
10-画一条0.5px的线
1.画一条1px党的线;
2.使用缩放属性,便可的到
transform: scale(0.5);
11-弹性盒属性
- flex是一种css盒子的布局方式,通过改变父元素的属性来控制子元素的排列方式,其常用属性有:
- flex-direction 主轴的方向
- flex-wrap 子元素空间不足时候是否换行
- justify-content 主轴对齐方式
- align-items 交叉轴的对齐方式
- align-content 多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用
flex:1 代表着什么
- flex:1 是 flex-grow、flex-shrink、flex-basis三个属性的缩写,一般默认为flex:1 1 auto(存在剩余空间就放大,空间不足就缩小);
- flex-grow:元素的放大比例,默认为1,即使存在剩余空间,也不会放大;
- flex-shrink:元素的缩小比例,默认为1,即如果空间不足,该元素将缩小;
- flex-basis:项目占据的主轴空间,默认为auto,即项目原本的大小。
二.JavaScript
1-js数据类型有哪些?
-
基本数据类型
// string:字符串, // Number:数值, // Boolean:布尔值, // undefined:未定义, // null:空类型, // symbol(ES6新增):符号,
// 复杂数据类型(引用数据类型)
// object:对象。包括function/array/object
2-找出数组中出现次数最多的元素,及出现的次数
let arr=[1,3,2,4,5,66,1,3,4,5,6,1,1,2,3,1,55,6,7,8,90,0];
方法一 、利用对象
-
1.创建空对象,遍历目标数组,根据数组元素是否存在于对象中,做对象属性的添加和次数的增加,
-
2.遍历完数组得出结果对象,再遍历对象找出最多元素和次数。
let arr = [
1, 3, 2, 4, 5, 66, 1, 3, 4, 5, 6, 1, 1, 2, 3, 1, 55, 6, 7, 8, 90, 0,
];
function findMost(arr) {
if (!arr.length) return;
if (arr.length === 1) return 1;
let res = {};
let maxName,
maxNum = 0;
// 遍历数组
arr.forEach((item) => {
res[item] ? (res[item] += 1) : (res[item] = 1);
});
// 遍历 res
for (let r in res) {
if (res[r] > maxNum) {
maxNum = res[r];
maxName = r;
}
}
return "出现次数最多的元素为:" + maxName + ", 出现次数为:" + maxNum;
}
console.log(findMost(arr));//出现次数最多的元素为:1, 出现次数为:5
方法二、?改良版
- 去除对象遍历,把比较放到数组遍历中
let arr = [
1, 3, 2, 4, 5, 66, 1, 3, 4, 5, 6, 1, 1, 2, 3, 1, 55, 6, 7, 8, 90, 0,
];
function findMost(arr) {
if (!arr.length) return;
if (arr.length === 1) return 1;
let res = {};
let maxName,
maxNum = 0;
// 遍历数组
arr.forEach((item) => {
res[item] ? (res[item] += 1) : (res[item] = 1);
if (res[r] > maxNum) {
maxNum = res[r];
maxName = r;
}
});
return "出现次数最多的元素为:" + maxName + ", 出现次数为:" + maxNum;
}
console.log(findMost(arr));//出现次数最多的元素为:1, 出现次数为:5
方法三、利用数组的reduce方法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
-
reduce方法接受两个参数,第一个是函数,第二个是初始值
-
函数内接受四个参数:计算后的结果或者初始值,当前值,当前下标,数组本身
function findMost (arr) {
if (!arr.length) return;
if (arr.length === 1) return 1;
let maxName, maxNum = 0
let res = arr.reduce((res, currentNum) => {
res[currentNum] ? res[currentNum] += 1 : res[currentNum] = 1
if (res[currentNum] > maxNum) {
maxNum = res[currentNum]
maxName = currentNum
}
return res
}, {})
return '出现次数最多的元素为:' + maxName + ', 出现次数为:' + maxNum;
}
3-js中栈内存和堆内存
js引擎中对变量的存储主要有两种位置,堆内存和栈内存。
-
栈内存主要用于存储各种基本类型的变量,包括Boolean、Number、String、Undefined、Null,**以及对象变量的指针,这时候栈内存给人的感觉就像一个线性排列的空间,每个小单元大小基本相等。
-
堆内存主要负责像对象Object这种变量类型的存储
-
栈内存中的变量一般都是已知大小或者有范围上限的,算作一种简单存储。而堆内存存储的对象类型数据对于大小这方面,一般都是未知的。
4-Url地址分为几个部分
主要分为:协议、域名、端口号、路径
对于这样一个URL
http://www.x2y2.com:80/fisker/post/0703/window.location.html?ver=1.0&id=6#imhere
我们可以用javascript获得其中的各个部分
1, window.location.href
整个URl字符串(在浏览器中就是完整的地址栏)
本例返回值: http://www.x2y2.com:80/fisker/post/0703/window.location.html?ver=1.0&id=6#imhere
2, window.location.protocol
URL 的协议部分
本例返回值:http:
3, window.location.host
URL 的主机部分
本例返回值:www.x2y2.com
4, window.location.port
URL 的端口部分
如果采用默认的80端口( update:即使添加了:80 ),那么返回值并不是默认的80而是空字符
本例返回值:""
5, window.location.pathname
URL 的路径部分(就是文件地址)
本例返回值:/fisker/post/0703/window.location.html
6, window.location.search
查询(参数)部分
除了给动态语言赋值以外,我们同样可以给静态页面,并使用javascript来获得相信应的参数值
本例返回值:?ver=1.0&id=6
7, window.location.hash
锚点
本例返回值:#imhere
5-面试题:实现获取URL搜索字段参数功能
1-js提供方法
URL = `?username=zhangsan&password=123456&type=get&type=post`;
function search(url) {
console.log(url);
let params = new URLSearchParams(url);
let username = params.get("username");
let password = params.get("password");
let type = params.getAll("type");
console.log(username, password);
console.log(type); // ["get", "post"]
let obj = {};
obj.username = username;
obj.password = password;
obj.type = type;
console.log(obj);
}
search(URL);
/* {username: "zhangsan", password: "123456", type: Array(2)}
password: "123456"
type: (2) ["get", "post"]
username: "zhangsan" */
2-数组、字符串方法
var search =
"?uname=dingdin&upwd=12345&favs=swimming&favs=running&favs=music";
function searchObj(str) {
//去掉?
var str = str.slice(1);
//根据“&”分割字符串
var arr = str.split("&");
//定义空的obj,保存对象
var obj = {};
//循环遍历分割后的数组
for (var p of arr) {
//根据“=”分割
var arr2 = p.split("=");
//解构
var [name, value] = arr2;
//如果obj中的name为undefined就把值填进去,否则就连接
if (obj[name] == undefined) {
obj[name] = value;
} else {
obj[name] = [].concat(value, obj[name]);
}
}
return obj;
}
var a = searchObj(search);
console.log(a);
/* {uname: "dingdin", upwd: "12345", favs: Array(3)}
favs: (3) ["music", "running", "swimming"]
uname: "dingdin"
upwd: "12345"
__proto__: Object */
6-跨域九种方法及解决方法
一、什么是跨域?
- 什么是同源策略?
- 浏览器不允许我们向别人发送请求,只能向自己的服务器发送请求
- 当我们想向别人的服务器发送请求的时候,就会被浏览器阻止了
- 什么是 “别人的服务器” 呢?
- 当 请求协议/域名/端口号 有任意一个不同的时候,那么就算是别人的服务器
- 这个时候就会触发同源策略
- 我们管触发了 同源策略 的请求叫做跨域请求
- 所谓同源是指"协议+域名+端口"三者相同,
- 什么时候产生跨域
- 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。
二、跨域解决方案
1、 通过jsonp跨域 2、 document.domain + iframe跨域 3、 location.hash + iframe 4、 window.name + iframe跨域 5、 postMessage跨域 6、 跨域资源共享(CORS) 7、 nginx代理跨域 8、 node.js中间件代理跨域 9、 WebSocket协议跨域
三、总结
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
- JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
- 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
- 日常工作中,用得比较多的跨域方案是cors和nginx反向代理
7-new干了什么?
执行过程
- 先创建了一个新的空对象;
- 设置原型,将对象的原型设置为函数的 prototype 对象;
- 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性);
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
具体实现:
function objectFactory() {
let newObject = null;
let constructor = Array.prototype.shift.call(arguments);
let result = null;
// 判断参数是否是一个函数
if (typeof constructor !== "function") {
console.error("type error");
return;
}
// 新建一个空对象,对象的原型为构造函数的 prototype 对象
newObject = Object.create(constructor.prototype);
// 将 this 指向新建对象,并执行函数
result = constructor.apply(newObject, arguments);
// 判断返回对象
let flag = result && (typeof result === "object" || typeof result === "function");
// 判断返回结果
return flag ? result : newObject;
}
// 使用方法
objectFactory(构造函数, 初始化参数);
8-什么是回流和重绘?
- 当DOM节点树中的一部分因为元素的规模尺寸,布局,隐藏等(几何属性)改变而需要重新构建,称为回流(重排)
- 当节点树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,称为重绘
- **注意:**回流必定会引发重绘,但重绘不一定引发回流,且回流代价更高
9-js执行上下文
JS代码在执行前,JS引擎总要做一番准备工作,这份工作其实就是创建对应的执行上下文;
执行上下文有且只有三类,全局执行上下文,函数上下文,与eval上下文;由于eval一般不会使用
1.全局执行上下文
// 全局执行上下文只有一个,也就是我们熟知的window对象,我们能在全局作用域中通过this直接访问到它
2.函数执行上下文
- 函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。
执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。
10-Es6的新特性
-
const与let
-
与var区别
-
let
和
const` 不允许重复声明变量 -
let
和
const` 声明的变量不会在预解析的时候解析(也就是没有变量提升) -
let
和
const` 声明的变量会被所有代码块限制作用范围
-
-
let
和
const` 的区别-
let
声明的变量的值可以改变,
const` 声明的变量的值不可以改变 -
let
声明的时候可以不赋值,
const` 声明的时候必须赋值
-
-
-
模板字符串
-
``(反引号):表示字符串;
-
和单引号还有双引号的区别
- 反引号可以换行书写
- 反引号可以直接在字符串里面拼接变量
-
-
解构赋值
-
解构**从数组和对象提取值并赋值给独特的变量
-
解构对象
const obj = { name: 'Jack', age: 18, gender: '男' } let { name, age, gender } = obj
前面的 { } 表示我要从 obj 这个对象中获取成员了
name age gender 都得是 obj 中有的成员
-
解构数组
const arr = ['Jack', 'Rose', 'Tom'] let [a, b, c] = arr
前面的 [ ] 表示要从 arr 这个数组中获取成员了
a b c 分别对应这数组中的索引 0 1 2
-
-
对象简写法
-
某个变量名作为对象属性的属性值时,当变量名和属性名一样时,只写变量名,此时:变量名作为属性名、变量值作为属性值;
-
方法简写:方法的名 作为对象的属性名,方法的值 作为对象的属性值
-
合并对象:
var zhangsan = {name:'zhangsan'}; var lisi = {age:18}; let res = Object.assign(zhangsan,lisi) console.log(zhangsan)//{name: "zhangsan", age: 18} console.log(res)//{name: "zhangsan", age: 18} console.log(lisi)//{age: 18}
-
-
展开运算符
-
... ,叫做展开运算符
-
作用是把数组展开
let arr = [1, 2, 3, 4, 5]//-> 1, 2, 3, 4, 5 console.log(...arr) // 1 2 3 4 5 console.log(1,2,3,4,5)
-
合并数组的时候可以使用
let arr = [1, 2, 3, 4] let arr2 = [...arr, 5] console.log(arr2)
-
也可以合并对象使用
let obj = { name: 'Jack', age: 18 } let obj2 = { ...obj, gender: '男' } console.log(obj2)
-
在函数传递参数的时候也可以使用
let arr = [1, 2, 3] function fn(a, b, c) { console.log(a) console.log(b) console.log(c) } fn(...arr) // 等价于 fn(1, 2, 3) function box(...arr){ console.log(arr) [1,2,3,4,5,6] } box(1,2,3,4,5,6)
-
-
剩余参数(可变参数)
使用展开运算符将数组展开为多个元素, 使用剩余参数可以将多个元素绑定到一个数组中. 剩余参数也用三个连续的点 (
...
) 表示,使你能够将不定数量的元素表示为数组.-
用途1: 将变量赋数组值时:
const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"]; const [total, subtotal, tax, ...items] = order; console.log(total, subtotal, tax, items);
-
用途2: 可变参数函数 对于参数不固定的函数,ES6之前是使用**参数对象(arguments)**处理
function sum() { let total = 0; for(const argument of arguments) { total += argument; } return total; } //在ES6中使用剩余参数运算符则更为简洁,可读性提高: function sum(...nums) { let total = 0; for(const num of nums) { total += num; } return total; }
-
-
ES6箭头函数
- 重点: **箭头函数只能简写函数表达式,不能简写声明式函数
- 箭头函数的特点
- 箭头函数内部没有 this,箭头函数的 this 是上下文的 this
- 箭头函数内部没有
arguments
这个参数集合 - 函数的行参只有一个的时候可以不写
()
其余情况必须写 - 函数体只有一行代码的时候,可以不写
{}
,并且会自动 return
-
参数默认值
- 就是当我不传递参数的时候,使用默认值,传递参数了就使用传递的参数
- 在 ES6 中我们可以直接把默认值写在函数的行参位置
- 这个默认值的方式箭头函数也可以使用
- 注意: **箭头函数如果你需要使用默认值的话,那么一个参数的时候也需要写 ()
11-Es5和Es6继承的区别
-
es5继承:基本思想:利用原型链让一个引用类型继承另一个引用类型的属性和方法(即通过prototype和构造函数实现) 实质:将父类添加到子类的原型链(或this)上去
-
ES5 的继承时通过原型或构造函数机制来实现
-
es6继承:基本思想:通过extend关键字实现继承,子类可以继承父类中所有的方法和属性,子类必须在construc()方法中调用super()方法,因为新建的子类没有自己的this对象,而是继承了父类的this对象;
-
ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关 键字实现继承。 5. 子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因 为子类没有自己的 this对象,而是继承了父类的 this 对象,然后对其进行加工。 如果不调用 super 方法,子类得不到 this 对象。 6. 注意 super 关键字指代父类的实例,即父类的 this 对象。 注意:在子类构造函数中,调用 super 后,才可使用 this关键字,否则报错
实质:利用extend关键字继承父类,然后继承父类的属性和方法
12-宏任务与微任务 js执行机制(事件循环)
(1)js执行机制(事件循环)
-
js是单线程的,代码是一句句执行的
所谓单线程就是一次只能执行一段代码,在执行的时候如果遇到比较耗时的操作,默认就会停下来等待这个操作完成之后继续走。这种情况下,就会出现页面卡在那里不动。为了解决这个问题js一般把比较耗时的操作做异步处理
-
js里面是有异步的
js中存在一个异步队列,所有比较耗时的操作都会被丢在这个异步队列中。当主线程空闲(同步代码)之后会执行异步队列中的代码,这个就是js中的eventloop(事件循环)。
(2)宏任务与微任务
1-宏任务 运行环境提供的异步操作 setTimeout setInterval setImedia Animareque
微任务 是js语言自身的异步操作 promise.then promise.catch
2-每一轮的宏任务执行完之后,如果有微任务就会先行本轮微任务,当所有的微任务都执行完成之后会进入下一个宏任务周期。
3-代码实现宏、微任务执行顺序
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
setTimeout(() => {
console.log(4);
}, 3);
var p = new Promise((resolve, reject) => {
console.log(5);
setTimeout(() => {
console.log(6);
resolve();
}, 1);
});
p.then((res) => {
console.log(7);
});
// => 1 3 5 2 6 7 4
13-防抖和节流
-
防抖:定义:当一段时间内连续多次触发某事件时,只执行最后一次。如果在设置的间隔时间内又触发事件,则清除定时器重新计时。
<button>点击一下</button>
var btn = document.querySelector("button"); // timer 全局变量 let timer = null; btn.onclick = function () { console.log(11); clearTimeout(timer); timer = setTimeout(() => { console.log("发送请求"); }, 500); };
-
节流:定义:当一段时间内连续多次触发某事件时,每个间隔时间内只能执行一次,过了间隔时间才能执行下一次。
<button>点击一下</button>
var btn = document.querySelector("button"); var flag = true; let count = 0; btn.onclick = function () { console.log(count + 1); if (flag) { flag = false; // 节流 间隔一秒 执行一次 console.log("发送请求"); setTimeout(() => { flag = true; }, 1000); } };
14-ES6数组新增的几个方法
关于数组中forEach() 、map()、filter()、reduce()、some()、every()的总结
-
1、forEach()
var arr = [1,2,3,4]; arr.forEach((item,index,arr) => { console.log(item) //结果为1,2,3,4 }) //forEach遍历数组,无返回值,不改变原数组,仅仅只是遍历、常用于注册组件、指令等等。
-
2、map()
var arr = [1,2,3,4]; arr.map((item,index,arr) => { return item*10 //新数组为10,20,30,40 }) //map遍历数组,返回一个新数组,不改变原数组的值。
-
3、filter()
var arr = [1,2,3,4]; arr.filter((item,index,arr) => { return item > 2 //新数组为[3,4] }) //filter过滤掉数组中不满足条件的值,返回一个新数组,不改变原数组的值。
-
4、reduce()
var arr = [1,2,3,4]; arr.reduce((result,item,index,arr) => { console.log(result) // 1 3 6 result为上次一计算的结果 console.log(item) // 2 3 4 console.log(index) // 1 2 3 return result+item //最终结果为10 },initValue) //reduce 让数组的前后两项进行某种计算。然后返回其值,并继续计算。不改变原数组,返回计算的最终结果,从数组的第二项开始遍历
-
5、some()
var arr = [1,2,3,4]; arr.some((item,index,arr) => { return item > 3 //结果为true }) //遍历数组每一项,有一项返回true,则停止遍历,结果返回true。不改变原数组
-
6、every()
var arr = [1,2,3,4]; arr.every((item,index,arr) => { return item > 1 //结果为false }) //遍历数组每一项,每一项返回true,则最终结果为true。当任何一项返回false时,停止遍历,返回false。不改变原数组
以上6个方法均为ES6语法,IE9及以上才支持。不过可以通过babel转意支持IE低版本。 以上均不改变原数组。
some、every返回true、false。 map、filter返回一个新数组。 reduce让数组的前后两项进行某种计算,返回最终操作的结果。 forEach 无返回值。
15-垃圾回收机制
-
标记清除算法
此算法可以分为两个阶段 ,一个是标记阶段( mark ),一个是清除阶段( sweep )
- 标记阶段
- 从根(全局)对象开始遍历,每一个可以从根对象访问到的对象都会被添加一个标记,于是这个对象被标记为可到达对象
- 清除阶段
- 对 堆内存 从头到尾进行线性遍历 如果发现对象 没有被标记 则将该对象占用的内存回收 并且将原来被标记的对象的标识清除
- 标记阶段
缺陷:
-
那些无法从根对象查询到的对象都将会被清除
-
垃圾收集后有可能会造成大量垃圾碎片
-
引入计数算法
跟踪记录每个值被应用的次数。
- 当声明一个变量 并将一个引用类型的值赋给该变量时 则这个值得引用次数就是1 如果同一个值又赋给另一个变量时 引用次数+1
- 相反 如果包含这个值的变量又取得另外一个值 那么该值得引用次数-1
- 当这个值得引用次数变成0的时候 则说明没有办法再访问这个值 当下次垃圾收集器下次再运行 就会清理掉引用次数为0的值
缺陷:
- 无法处理循环引用。 如果两个对象创建并互相引用 形成一个循环。考虑到他们互相都有一次引用,所以不会被回收
16-手写promise
var a1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("1秒钟");
}, 1000);
});
var a2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("3秒钟");
}, 3000);
});
var a3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("5秒钟");
}, 5000);
});
// a1.then((res) => {
// console.log(res);
// }).catch((err) => {
// console.log(err);
// });
// 所有的都执行成功,最后统一返回
// Promise.all([a2, a1, a3])
// .then((res) => {
// console.log(res);
// })
// .catch((err) => {
// console.log(err);
// });
// 竞速
// Promise.race([a3, a2, a1]).then((res) => {
// console.log(res);
// });
var aa = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("这是执行结果");
}, 1000);
});
// aa.then((res) => {
// console.log(res);
// return 123;
// })
// .then((res) => {
// console.log(res);
// return 456;
// })
// .then((res) => {
// console.log(res);
// });
// async await
async function text() {
const result = await aa;
console.log(result);
console.log(1111);
}
text();
// function* aa(){}
17-深拷贝和浅拷贝
要考虑基本数据类型和引用数据类型
- 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
- 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响(完全拷贝)
- 总的来说,深浅拷贝的主要区别就是:复制的是引用还是复制的是实例
浅拷贝 方法:for Object.assgin ...
深拷贝 方法:递归(循环引用 爆栈-》 Map WeakMap) json.parse stringify (不能拷贝函数)
lodash
var obj = {
name: "zhangsan",
age: 28,
aaa() {},
child: {
name: "zhangsanfeng",
age: 1,
child: { name: "zhangwuji", age: 0 },
},
};
1.lodash方法
let res1 = _.clone(obj);// 浅拷贝
let res2 = _.cloneDeep(obj);// 深拷贝
2.json.parse stringify (不能拷贝函数)
// 一句话 深拷贝
let cloneObj = JSON.parse(JSON.stringify(obj)); //不能复制方法
3.Object.assign()
// 浅拷贝 拷贝 如果属性是对象 - 直接拷贝的是地址
Object.assign(cloneObj, obj); //浅拷贝
4.for
let cloneObj = { ...obj };
// 浅拷贝
for (let prop in obj) {
prop; //属性名
obj[prop]; //属性值
console.log(prop, obj[prop]);
cloneObj[prop] = obj[prop];
}
5.递归
// 深拷贝 如果属性是对象 - 创建对象-拷贝属性和属性值
function deepClone(obj) {
let cloneObj = {};
for (let prop in obj) {
prop; //属性名
obj[prop]; //属性值
console.log(prop, obj[prop]);
let value = obj[prop];
if (typeof value == "object") {
value; //是一个对象
// 创建一个对象 跟 value 一样
cloneObj[prop] = deepClone(value);
} else {
//cloneObj[name] = zhangsan
cloneObj[prop] = value;
}
}
return cloneObj;
}
18-get和post的区别
-
get传递数据在url中,长度有限制
-
post传递的数据在请求体中,长度相对大一些
-
post请求主要用来传递大量数据做新增操作
-
get方式,它会被缓存在浏览器当中
-
post方式,不会在我们的浏览器中缓存
-
get用来获取,post用来发送
-
post请求主要用来传递大量数据做新增操作
19-闭包
闭包就是能够读取其他函数内部变量的函数
在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
定义:
- 有一个 A 函数,再 A 函数内部返回一个 B 函数
- 再 A 函数外部有变量接收这个 B 函数
- B 函数内部访问着 A 函数内部的私有变量
闭包实现:
function fmn() {
var a = 1;
function test() {
// console.log(a);
a++;
//a 作用域使用变量a的时候,首先在自身找,找不到 父级作用域找 -》直到全局作用域
// 作用域链
return a;
}
return test;
}
//res 就是test 函数
// res->test -> a
//res 是全局变量 test空间不会被销毁 ,test执行空间使用a, a不会被销毁
var res = fmn();
console.log(res);// =>a=2
// 让局部变量 像是全局变量一样 , 不能随意更改,局部变量被保护了
20-闭包的优缺点
为什么要叫做特点,就是因为他的每一个点都是优点同时也是缺点
- 作用域空间不销毁
- 优点: 因为不销毁,变量页不会销毁,增加了变量的生命周期
- 缺点: 因为不销毁,会一直占用内存,多了以后就会导致内存溢出
- 可以利用闭包访问再一个函数外部访问函数内部的变量
- 优点: 可以再函数外部访问内部数据
- 缺点: 必须要时刻保持引用,导致函数执行栈不被销毁
- 保护私有变量
- 优点: 可以把一些变量放在函数里面,不会污染全局
- 缺点: 要利用闭包函数才能访问,不是很方便
21-什么是内存泄漏
内存泄露是指:内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。//(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
//二:常见的内存泄露造成的原因
1、单例造成的内存泄漏
2、非静态内部类创建静态实例造成的内存泄漏
3、Handler造成的内存泄漏
示例:创建匿名内部类的静态对象
4、线程造成的内存泄漏
5、资源未关闭造成的内存泄漏
22-那些操作或造成内存泄漏
- 闭包
- 意外的全局变量(不知道什么时候写出来的导致一直被引用)
- 被遗忘的定时器
- 没有清理的DOM元素引用(之前获取的dom元素,后来被删除,但是内存中还保存着对元素的引用)
23-原型和原型链
// 原型:每个构造函数都有一个原型属性 prototype 保存了所有用构造函数实例化出来对象的方法,用构造函数创建一个对象,这个对象上会有一个属性__proto__ 指向构造函数的原型属性,
// 原型链:当我们使用一个对象的属性或者方法的时候,首先在自身内存查找,找到就用,找不到就去原型上找。原型的本质是对象,对象又有自己的原型 ,原型如果没有这个方法,就去原型的原型中查找,这个查找的链条就叫做原型链,
// 最后找到Object.prototype 如果Object.prototype 没有这个属性,直接返回undefind,因为Object.prototype 的原型是null。
24-js事件传播流程
事件捕获阶段、处于目标阶段、事件冒泡阶段。
(1)捕获阶段:事件从根节点流向目标节点,途中流经各个DOM节点,在各个节点上触发捕获事件,直到达到目标节点。
(2)目标阶段:在此阶段中,事件传导到目标节点。浏览器在查找到已经指定给目标事件的监听器后,就会运行该监听器。
(3)事件冒泡: 当为多个嵌套的元素设置了相同的事件处理程序,它们将触发事件冒泡机制。在事件冒泡中,最内部的元素将首先触发其事件,然后是栈内的下一个元素触发该事件,以此类推,直到到达最外面的元素。如果把事件处理程序指定给所有的元素,那么这些事件将依次触发。
25-es6变量提升是什么:
ES6 一共有 6 种声明变量的方法,即var / function / let / const / import / class var 声明的变量作用域是全局的或者是函数级的。 var定义的变量可以修改,如果不初始化会输出undefined,不会报错。 const定义的变量,一般在require一个模块的时候用或者定义一些全局常量。 let只能声明一次,var可以声明多次
// 关于变量提升:
1、只有声明本身会被提升,而赋值操作不会被提升。
2、变量会提升到其所在函数的最上面,而不是整个程序的最上面。
3、函数声明会被提升,但函数表达式不会被提升。
4、函数声明的优先级高于普通变量申明的优先级,并且函数声明和函数定义的部分一起被提升。
5、同一个变量只会声明一次,其他的会被忽略掉。
26-this指向问题
//- 普通函数调用,此时this指向window
//- 构造函数调用,this指向实例对象
//- 对象方法调用,this指向该方法所属对象
//- 事件绑定时,this指向绑定事件的对象
//- 定时器函数,this指向window
//- 箭头函数,this指向上下文
更改this指向的方法
// 三种:
1.call( this指向的对象, 参数1, 参数2, 参数3, ...)
2.apply(this指向的对象, [参数1, 参数2, 参数3, ...])
3.bind(this指向的对象, 参数1, 参数2, 参数3, ...)
// 相同点:
1./* 改变函数调用时内部this的指向,第一个参数都是函数内部this指向的对象 */
// 不同点:
1.call从第二个参数开始为函数的参数,而且参数是单一传递,不能以参数数组传递;
2.apply从第二个参数开始为函数的参数,而且第二个参数为数组,该数组包含函数的所有参数;
3.bind从第二个参数开始为函数的参数,而且/*参数是单一传递,*/不能以参数数组传递,并且返回一 个绑定函数内部this指向的函数(即使用时不会立即调用一次返回的函数 )
bind 是创建一个新的函数,我们必须要手动去调用
27-页面渲染逻辑
1.HTML和CSS经过各自解析,生成DOM树和CSSOM树
2.合并成为渲染树
3.根据渲染树进行布局
4.最后调用GPU进行绘制,显示在屏幕上
28-浏览器缓存 强缓存 协商缓存
缓存策略
/*强缓存:*/ 不会向服务器发送请求,直接从缓存中读取资源,
/*协商缓存:*/ 就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
29-promise请求封装
export default function ajax(url, data = {}, type = "GET") {
return new Promise((resolve, reject) => {
let promise;
if (type === "GET") {
// get请求
promise = axios.get(url, { params: data }); // 这个本来就是一个promise对象
} else {
promise = axios.post(url, data);
}
promise
.then((response) => {
resolve(response.data);
})
.catch((error) => {
message.error("请求出错了: " + error.message);
});
});
}
30-判断是不是数组
方法一: instanceof:
var arr=[];
console.log(arr instanceof Array) //返回true
方法二: constructor:
console.log(arr.constructor == Array); //返回true
方法三: Array.isArray()
console.log(Array.isArray(arr)); //返回true
方法四: Object.prototype.toString.call()
console.log(Object.prototype.toString.call(arr)) //"[object Array]"
31-从输入网址到页面显示的过程
1.URL输入
2.DNS解析
3.TCP连接
4.发送HTTP请求
5.服务器处理请求
6.服务器响应请求
7.浏览器解析渲染页面
8.连接结束
32-JS的异步操作有哪些
1.定时器
2.事件绑定
3.AJAX
4.回调函数
33-promise和async的区别
async可以return await的promise对象
1.函数前面多了一个aync关键字。
2.await关键字只能用在aync定义的函数内。
3.async函数会隐式地返回一个promise,该promise的resolve值就是函数return的值。
// 第二种看法
1. Promise是ES6中处理异步请求的语法,使用.then()来实现链式调用,使用.catch()来捕获异常。
2. async/await 是对Promise的升级,async用于声明一个函数是异步的,await是等待一个异步方法执行完成(await一个Promise对象),/*async/await的捕获异常可以使用try/catch语法。*/(也可以使用.catch语法)
34-常见设计模式
单例模式、观察者模式、组合模式
35-模块化开发
模块化开发:指文件的组织、管理、使用的方式。即把一个大的文件拆分成几个小的文件,他们之间相互引用、 依赖
优点:
1.可以加速渲染页面,所有资源加载的时间不会因为模块化而加速,但是模块化能加速渲染
2.避免命名冲突
3.代码重用高
4.思路更为清晰,降低代码耦合
36-es和common.js 区别
1、es 是静态引入,编译时引入 //异步
es6 {
export: '可以输出多个,输出方式为 {}' ,
export default : ' 只能输出一个 ,可以与export 同时输出,但是不建议这么做',
解析阶段确定对外输出的接口,解析阶段生成接口,
模块不是对象,加载的不是对象,
可以单独加载其中的某个接口(方法),
静态分析,动态引用,输出的是值的引用,值改变,引用也改变,即原来模块中的值改变则该加载的值也改变,
this 指向undefined
}
2、common.js 动态引入,执行时引入 //同步导入
commonJS {
module.exports = ... : '只能输出一个,且后面的会覆盖上面的' ,
exports. ... : ' 可以输出多个',
运行阶段确定接口,运行时才会加载模块,
模块是对象,加载的是该对象,
加载的是整个模块,即将所有的接口全部加载进来,
输出是值的拷贝,即原来模块中的值改变不会影响已经加载的该值,
this 指向当前模块
}
37-for in / for of 区别 返回值
一般用for in遍历对象 、//返回键 遍历数组返回下标
for of 遍历数组 ; //返回每一项值
也不是说不能用for in遍历数组、只是用for in遍历数组的时候会遍历数组所有的可枚举属性、也包括它的原型方法、也就是说for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值;for of的话不能遍历对象、因为没有迭代器对象(注:迭代器,提供一种访问一个集合对象各个元素的途径,同时又不需要暴露该对象的内部细节)
for in 返回对象的key或者数组、字符串的下标; for of和forEach一样直接返回值
38-XSS攻击,CRFS攻击
// XSS攻击:
攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
防范XSS攻击:
httpOnly: 在 cookie 中设置 HttpOnly 属性后,js脚本将无法读取到 cookie 信息。
输入过滤:
1:检测按照规定的格式输入
2:转义HTML,把引号,尖括号,斜杠进行转义
// CRFS攻击:
一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。
防御CSFS攻击:
验证码:强制用户必须与应用进行交互,才能完成最终请求。
设置token
39-http常见状态码
- 200:服务器已经成功处理了请求。
- 3XX:表示重定向。
- 401:未授权,服务器请求身份验证。
- 404:服务器找不到请求的网页。
- 500:服务器内部错误,无法完成请求。
- 503:服务器目前无法使用。
40-js中forEach和map的区别?
- 两者都可以用来遍历数组,forEach无返回值,map会映射并返回一个新的数组。
- forEach会允许修改原数组的值,而map不能修改原数组的值。
42-面向对象
OOP,面向对象的编程啊,还有OOD(面向对象的设计),OOA(面向对象的分析)
/*面向过程*/ // 是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现。
/*面向对象*/ // 是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现
特点:// 继承 封装 多态
43-数组去重
1、使用ES6中的set方法
2、indexOf()方法
3、利用数组的sort()方法(相邻元素比对法)
4、利用数组的includes
5、利用双重for循环、再用splice方法去重(ES5常用)
、函数递归
-
1、使用ES6中的set方法
function unique10(arr) { //Set数据结构,它类似于数组,其成员的值都是唯一的 return Array.from(new Set(arr)); // 利用Array.from将Set结构转换成数组 } console.log(unique10([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])); // 结果是[1, 2, 3, 5, 6, 7, 4]
-
2、indexOf()方法
function unique4(arr) { var newArr = [] for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i])===-1) { newArr.push(arr[i]) } } return newArr } console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])); // 结果是[1, 2, 3, 5, 6, 7, 4]
-
3、利用数组的sort()方法(相邻元素比对法)
// 先将原数组排序,在与相邻的进行比较,如果不同则存入新数组。 function unique2(arr) { var formArr = arr.sort() var newArr=[formArr[0]] for (let i = 1; i < formArr.length; i++) { if (formArr[i]!==formArr[i-1]) { newArr.push(formArr[i]) } } return newArr } console.log(unique2([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])); // 结果是[1, 2, 3, 4,5, 6, 7]
-
4、利用数组的includes
function unique5(arr) { var newArr = [] for (var i = 0; i < arr.length; i++) { if (!newArr.includes(arr[i])) { newArr.push(arr[i]) } } return newArr } console.log(unique5([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])); // 结果是[1, 2, 3, 5, 6, 7, 4]
-
5、利用双重for循环、再用splice方法去重(ES5常用) 、函数递归
function unique8(arr) { var i,j,len = arr.length; for (i = 0; i < len; i++) { for (j = i + 1; j < len; j++) { if (arr[i] == arr[j]) { arr.splice(j, 1); len--; j--; } } } return arr; } console.log(unique8([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4]));
git常用命令
// 把远程仓库和本地仓库关联起来(SSH公钥方式关联) 1.git remote add origin git@github.com:ldf-fan/project-team.git // 把本地仓库代码推送到远程仓库的master分支 2.git push -u origin master // 把远程仓库代码下载(克隆)到本地终端 3.git clone 仓库地址(ssh地址、http地址等) // 查看分支 4.git branch // 切换到dev分支,如没有该分支,则创建分支,并切换到该分支 5.git checkout -b dev // 查看状态(变化) 6.git status // 把本地所有代码(修改后)添加到缓存区 7.git add . // 把缓存区的代码(内容)提交到历史区 (最好备注本次提交做了那些修改) 8.git commit -m "第一次修改" // 切换到master分支(master即主分支) 9.git checkout master // 查看本地和远程所有分支 10.git branch -a // 把dev分支合并到当前分支(内容) 11.git merge dev(重要) // 删除本地xxx分支 12.git branch -d xxx // 先将远程仓库最新内容(版本)拉曲到当前本地分支直接合并 目的确保本地推送到远程时远程仓库是最新版本代码 (如果修改相同文件可能会发生冲突) 13.git pull // 把本地代码推送到远程仓库 14.git push
三.Vue.js
vue
1-数据双向绑定原理
- vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
- 核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法
- 介绍一下Object.defineProperty()方法 (1)Object.defineProperty(obj, prop, descriptor) ,这个语法内有三个参数,分别为 obj (要定义其上属性的对象) prop (要定义或修改的属性) descriptor (具体的改变方法) (2)简单地说,就是用这个方法来定义一个值。当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,又用到了它里面的set方法;
优点:
-
用户在视图上的修改会自动同步到数据模型中去,数据模型中值的变化也会立刻同步到视图中去;
-
无需进行和单向数据绑定的那些相关操作;
-
在表单交互较多的场景下,会简化大量业务无关的代码。
缺点:
- 无法追踪局部状态的变化;
代码实现:
<div id="app">
<h1 v-text="num"></h1>
<p v-text="num"></p>
<input v-model="num" />
</div>
<script>
var obj = { _num: 0 };
Object.defineProperty(obj, "num", {
get() {
console.log("get执行了");
return obj._num;
},
set(v) {
console.log("set执行了");
obj._num = v;
console.log(obj._num);
var elems = document.querySelectorAll("*[v-text='num']");
for (var i = 0; i < elems.length; i++) {
elems[i].innerHTML = v;
}
var els = document.querySelectorAll("*[v-model='num']");
for (var i = 0; i < els.length; i++) {
els[i].value = v;
els[i].oninput = function (e) {
obj.num = e.target.value;
};
}
},
});
obj.num;
obj.num = 100;
</script>
2-.once应用场景
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
.stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡;
.prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
.capture:与事件冒泡的方向相反,事件捕获由外到内;
.self:只会触发自己范围内的事件,不包含子元素;
3-手写Mvvm
<!-- 如上 1-数据双向绑定原理 -->
4-MVC和MVVM的区别 www.jianshu.com/p/b0aab1ffa…
MVC:包括view视图层、controller控制层、model数据层。一句话描述就是Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View,
**MVVM:**包括view视图层、model数据层、viewmodel层(实例化对象)。
-
vue是实现了双向数据绑定的mvvm框架,当视图改变更新模型层,当模型层改变更新视图层。在vue中,使用了双向绑定技术,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。
-
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。
mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。和当 Model 频繁发生变化,开发者需要主动更新到 View 。
5-什么是虚拟dom?
- vdom可以看作是一个使用js模拟了DOM结构的树形结构,是一层对真实 DOM 的抽象。可理解为一个简单的JS对象。
6-Vue中key值的作用?
-
需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。
-
作用主要是为了高效的更新虚拟DOM。
7-面试题:v-if和v-show的区别
答: /*共同点:*/ 都能控制元素的显示和隐藏;
/*不同点:*/ 实现本质方法不同,
v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;
v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。
总结:如果要频繁切换某节点,使用v-show(切换开销比较小,初始开销较大)。如果不需要频繁切换某节点使用v-if(初始渲染开销较小,切换开销比较大)。
8-Vue.js 生命周期
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染(更新Dom)、销毁等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。
每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。在执行过程中会触发不同阶段的生命周期钩子。
-
1.beforeCreate(){}:实例初始化之后,数据观测和事件配置之前被调用 // 这是遇到的第一个生命周期函数表示实例完全会被创建出来,会执行 // 注意在beforeCreate生命周期函数执行的时候,data和methods中的数据都还没有被初始化 2.created(){}:实例创建完成后被立即调用 // 实例创建阶段,这是第二个生命周期函数 //这个生命周期阶段用来调用接口,初始化数据 //在created中,data和methods都已经初始化好了 //如果要调用methods中的方法,最早只能在created中操作 3.beforeMount(){}:挂载开始之前被调用:相关的 render 函数首次被调用 //这是遇到的第3个生命周期函数,表示模板已经编译完成,但是尚未把模板渲染到页面中去 //在beforeMount执行的时候,页面中的元素没有被真正替换过来,只是之前的一些模板字符串 4.mounted(){}:实例被挂载后调用 //这是遇到的第四个生命周期函数,表示内存中的模板,已经真实的挂载到了浏览器的页面中,用户已经看到了渲染好的页面 //注意:mounted是实例创建中的最后一个生命周期函数,当执行完mounted,实例就完全被创建好了 5.beforeUpdate(){}:组件或实例的数据更改之后,会立即执行beforeUpdate //数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器 6.updated(){}: // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 // 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。 7.beforeDestroy(){}: // 实例销毁之前调用。在这一步,实例仍然完全可用。 // 当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等 8.destroyed(){}: // 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
-
demo
<div id="app"> <input type="button" @click="msg='nono'" value="按钮" /> <p>{{msg}}</p> <button @click="xiaohui">销毁</button> </div>
var vm = new Vue({ el: "#app", data: { msg: "ok", }, methods: { show() { console.log("我执行了!"); }, xiaohui() { vm.$destroy();//完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。 }, }, beforeCreate() { // 这是遇到的第一个生命周期函数表示实例完全会被创建出来,会执行 console.log(this.msg); //undefined this.show(); //this.show is not a method // 注意在beforeCreate生命周期函数执行的时候,data和methods中的数据都还没有被初始化 }, // 实例创建阶段,这是第二个生命周期函数 created() { //这个生命周期阶段用来调用接口,初始化数据 //在created中,data和methods都已经初始化好了 //如果要调用methods中的方法,最早只能在created中操作 console.log(this.msg); this.show(); console.log(document.querySelector("p").innerHTML); this.$nextTick(() => { console.log(document.querySelector("p").innerHTML); }); }, beforeMount() { //这是遇到的第3个生命周期函数,表示模板已经编译完成,但是尚未把模板渲染到页面中去 console.log(document.querySelector("p").innerHTML); console.log("msg", this.msg); //在beforeMount执行的时候,页面中的元素没有被真正替换过来,只是之前的一些模板字符串 }, mounted() { //这是遇到的第四个生命周期函数,表示内存中的模板,已经真实的挂载到了浏览器的页面中,用户已经看到了渲染好的页面 console.log("mounted", document.querySelector("p").innerText); //注意:mounted是实例创建中的最后一个生命周期函数,当执行完mounted,实例就完全被创建好了 }, beforeUpdate() { //数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM console.log("beforeUpdate", this.msg); console.log("mounted", document.querySelector("p").innerText); }, updated() { // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 // 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。 console.log("beforeUpdate", this.msg); console.log("mounted", document.querySelector("p").innerText); }, beforeDestroy() { // 实例销毁之前调用。在这一步,实例仍然完全可用。 console.log("销毁前"); }, destroyed() { // 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。 console.log("销毁后"); }, });
9-vm.$nextTick()解决特效渲染问题
- 当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。
10-组件
-
组件有自己的data和methods;
-
组件里的data必须是一个函数,而且有一个返回值;
-
组件也有自己的生命周期;
-
在Vue中只能有一个根组件;
-
注意组件的执行顺序问题;
11-子组件如何改变父组件
注:组件信息流转的时候只能单向 1 > 父子传参 传参:通过属性 prop:传递数据
父组件通过props向下传递数据给子组件
父向子传递数据是通过 props,子向父是通过 events($emit
);
通过父链 / 子链也可以通信($parent
/ $children
);
ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
<div id="app">
<p>{{msg}}</p>
<child
@func="changemmsg"
title="我是标题"
xxx="什么东西"
:parentmsg="msg"
></child>
<transition @before-enter="" @enter="" :duration="{}"></transition>
</div>
<script>
Vue.component("child", {
props: ["title", "xxx", "parentmsg"], // props接受父组件传递过来的单数
template: `<div>
<p>这是一个四数据,标题是{{title}}</p>
<p>{{xxx}}</p>
<p @click="changemsg">父组件传递过来的msg---{{parentmsg}}</p>
</div>`,
data() {
return {};
},
methods: {
changemsg() {
// this.$emit("func", "我是子组件修改父组件后的值");
this.$emit("func", {
str: "子组件准备修改父组件",
msg: "ok",
});
},
},
});
new Vue({
el: "#app",
data: {
msg: "我是父组件的msg",
},
methods: {
changemmsg(str) {
console.log(str);
this.msg = str.str;
},
},
});
12-method,watch,computed
watch: 支持异步,不依赖缓存,一对多,一旦发生改变就会执行 computed: 不支持异步 依赖缓存 多对一 当依赖项发生改变才会触发更改
13-路由跳转传参方式
vue自带的路由传参的三种基本方式
场景:点击当前页的某个按钮跳转到另外一个页面去,并将某个值带过去
<div class="examine" @click="insurance(2)">查看详情</div>
-
第一种方法 页面刷新数据不会丢失
methods:{ insurance(id) { //直接调用$router.push 实现携带参数的跳转 this.$router.push({ path: `/particulars/${id}`, }) } // 需要对应路由配置如下: { path: '/particulars/:id', name: 'particulars', component: particulars } // 可以看出需要在path中添加/:id来对应 $router.push 中path携带的参数。在子组件中可以使用来获取传递的参数值 // 另外页面获取参数如下 this.$route.params.id
-
第二种方法 页面刷新数据会丢失
通过路由属性中的name来确定匹配的路由,通过params来传递参数。
methods:{ insurance(id) { this.$router.push({ name: 'particulars', params: { id: id } }) } // 对应路由配置: 注意这里不能使用:/id来传递参数了,因为组件中,已经使用params来携带参数了。 { path: '/particulars', name: 'particulars', component: particulars }
子组件中: 这样来获取参数
this.$route.params.id
-
第三种方法 使用path来匹配路由,然后通过query来传递参数 这种情况下 query传递的参数会显示在url后面?id=?
{ path: '/particulars', name: 'particulars', component: particulars } // 对应子组件: 这样来获取参数 this.$route.query.id
尤其注意:组件中 获取参数的时候是router 这很重要
14-keep-alive
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。一旦使用keepalive包裹组件,此时mouted,created等钩子函数只会在第一次进入组件时调用,当再次切换回来时将不会调用。此时如果我们还想在每次切换时做一些事情,就需要用到另外的周期函数,actived和deactived,这两个钩子函数只有被keepalive包裹后才会调用。
15-vue性能优化
1、// v-for和v-if不要在同一级使用,v-for比v-if的优先级高 会先执行循环,同时使用了会每次循环都执行一次v-if,
2、// vue 中使用的js资源 尽量的使用cdn方式在加载,以此减少打包之后的体积
3. // 路由懒加载
{
path: '/',
name: 'home',
component: () => import('./views/home/index.vue'),
meta: { isShowHead: true }
}
4、// 组件的按需引入,例如element ui ant Design等框架提供的都有按需引入 ,需要哪些功能引入哪些组件。
5、// 图标和图片的合理性优化
1.小图标使用 SVG
2.通过 base64 和 webp 的方式加载小型图片
3.大图片尽量使用CND的方式加载
4.图片的懒加载,大部分ui框架都带有图片懒加载的,没有可以使用v-lazy
6. // 开启 Gzip 就是一种压缩技术,需要前端提供压缩包,然后在服务器开启压缩,文件在服务器压缩后传给浏览器,浏览器解压后进行再进行解析。
7. // keep-alive页面缓存 keep-alive 会缓存当前页面的数据,避免重复调用接口 重复渲染占用性能
<keep-alive :include="tagsList">
<router-view :key="$route.fullPath"></router-view>
</keep-alive>
8、// 避免内存泄漏
9、// 减少 ES6 转为 ES5 的冗余代码
10、// 代码层面优化
(1) computed 和 watch 区分使用场景
(2) v-if 和 v-show 区分使用场景 v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。这里要说的优化点在于减少页面中 dom 总数,我比较倾向于使用 v-if,因为减少了 dom 数量。
16-Vue父子组件生命周期调用顺序
- 加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount>子mounted->父mounted
- 子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程
父beforeUpdate->父updated
- 销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
- 理解:
组件的调用顺序都是先父后子,渲染完成的顺序肯定是先子后父
组件的销毁操作是先父后子,销毁完成的顺序是先子后父
vue-router
vuex
四.React
react
1-判断this.setState( )是同步还是异步
可以是同步也可以是异步
- 在合成事件里面是异步
(1)在合成事件里面修改状态 (2)在生命周期里面修改状态的时候
-
在原生事件里面是同步
(1)在原生操作里面 (2)setTimeout里面也是同步的
批量处理 (一旦状态改变,react组件就会重新渲染)
2-受控组件和非受控组件
使用状态控制的表单元素为受控组件 (受控组件一般都是对于表单而言的)
3-function组件和class组件的区别
定义组件有两个要求 (1)组件名必须大写 (2)组件的返回值只能有一个根元素
-
无论是使用函数组件或者是类组件来声明一个组件,它绝不能修改props
-
所有React组件都必须是纯函数,禁止修改自身的props
-
react是单项数据流,父组件改变属性,子组件视图就会更新
-
props是传递过来的,state是自身的
区别:
- 函数组件比类组件性能高
- 类组件使用的时候需要实例化,函数组件直接执行返回即可,为了提高性能尽量使用函数组件。
- 函数组件没有this,没有生命周期,没有state,使用hooks模拟局部状态和生命周期函数
4-react组件传参
-
组件间传参
// 使用context上下文,通过使用context.Provider数据提供者来实现全局的数据共享。
-
1、父传子
// 父传子将父组件中的属性传入子组件,子组件可以用props直接接收并使用。 改变不了props里面的数据的,props里面的数据只读
-
2、子修改父
// 定义一个方法,把这个方法传递给子组件,子组件通过调用这个方法改变父组件的值
代码实现:
class App extends Component { constructor(props) { super(props); this.state = { count: 10, }; } addcount = () => { this.setState({ count: this.state.count + 1, }); }; // 定义一个方法,把这个方法传递给子组件,子组件通过调用这个方法改变父组件的值 changecount = (num) => { this.setState({ count: num, }); }; render() { const { count } = this.state; return ( <div> <span>{count}</span> <button onClick={this.addcount}>count++</button> <Child name="张三" age="20" count={count} changecount={this.changecount} /> </div> ); } } class Child extends Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div> 我是子组件 <span>{this.props.name}</span> <span>年龄是{this.props.age}</span> <span>count的值是{this.props.count}</span> <button onClick={() => this.props.changecount(1000)}> 修改props中的count </button> </div> ); } }
5-refs
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
-
class组件ref
ref (1) ref="aaa" (2) ref={(span)=>{this.span=span}} (3) this.span = React.createRef() <Child ref={this.span} /> // 可以通过调用组件的属性或者方法获取子组件的值或者执行逻辑代码
6-React生命周期(class组件)(老版)
-
挂载阶段
初始化阶段 constructor()//初始化阶段主要是props和state componentWillMount//将要挂载,这个生命周期只执行一次 ,一般用于一次性规定的数据 render//开始渲染 componentDidMount//挂载完成,发生在render之后,这个生命周期只执行一次,在这个阶段主要是发起ajax请求,请求数据,订阅消息等
-
运行中阶段
//第一条路 props发生改变 componentWillReciverProps()//子组件状态接收到参数,组件将要接受参数(传递过来的参数),接受到最新的props shouldComponentUpdate//这是一个阀门,组件应该更新吗,返回true时继续更新,返回false停止更新,不在往下走,(可做性能优化) componentWillUpdate()//组件将要更新 render//组件渲染 初始化会执行,props或者state发生改变也会执行 componentDidUpdate//组件已经更新过 //第二条路 state发生改变 useState()//组件更新时 shouldComponentUpdate//这是一个阀门,组件应该更新吗,返回true时继续更新,返回false停止更新,不在往下走,(可做性能优化,里面有两个参数nextProps和nextState) componentWillUpdate()//组件将要更新 render//组件渲染 componentDidUpdate//组件已经更新过,(在这个里面可以获取最新的state状态,props) //第三条路不经过阀门,直接到componentWillUpdate() force.update()//强制更新 componentWillUpdate()//组件将要更新 render//组件渲染 componentDidUpdate//组件已经更新过
-
销毁阶段
componentWillUnMount//卸载阶段,可以取消定时器和取消订阅消息等
注意新版生命周期后废弃了 componentWillMount componentWIllReceiveProps componentWillUpdate
16.3之后新增的生命周期函数是getDerviedStateFromProps和getSnapshotBeforeUpdate 删除了componentwillreceiverprops
7-什么是react-hooks
- React-Hooks 是一套能够使函数组件更强大、更灵活的“钩子”。
解决了什么问题?
1.它可以让你在不编写class的情况下使用state以及其他的React特性,
2.Hook 使你在无需修改组件结构的情况下复用状态逻辑
8-react diff 原理(常考,大厂必考)
- 把树形结构按照层级分解,只比较同级元素。
- 给列表结构的每个单元添加唯一的 key 属性,方便比较。
- React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
- 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
9-react里面key的作用
Keys是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识. 使diff算法更高效
//在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性
10-什么是纯函数
1.相同的输入永远返回相同的输出,
2.不修改函数的输入值,
3.不依赖外部环境状态。
4.无任何副作用。
11-webpack打包 原理 那些插件
打包原理:webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖的文件,使用loader来处理它们,最后打包成一个或多个浏览器可识别的js文件
10-jsx语法
就是在js中直接写html标签
在jsx语法中如果需要展示一个数据在页面中 需要使用{}(花括号)
11-常见hooks
// useState,useEffect,useReducer,useContext,useMemo,useCallback,useRef
1.useState
// useState可以在function定义的组件中设置一个局部状态
// 当状态数据改变之后,组件重新渲染
2.useEffect (副作用)
(1) 当数据改变之后触发的一些其他操作
(2) useEffect接收两个参数 第一个参数回调函数 第二个参数是一个数组
(3) 数组里面的内容是依赖项 当依赖项数组发生改变之后这个回调函数会执行
(4) 如果依赖数组为空 表示只执行一次
(5) useEffect第一个参数可以返回一个function 这个方法在组件销毁的时候执行一次
3.useCallback
(1) 接收两个参数 参数一是一个function 参数二是一个依赖数组
(2) 它可以对方法做一个缓存 当依赖数组中的数据没有发生改变的时候不会重新赋值
(3) 尽量多用 可以减少内存空间
4.useMemo
(1) 可以对数据做缓存 返回的是一个值 接受两个参数
(2) 参数一是一个function 计算结果
(3) 参数二是一个依赖数组
(4) 是对计算结果做缓存 当依赖数组不发生改变的时候,不会重新赋值
5.useRef
(1) 获取当前组件的dom元素
6.useReducer
(1) 使用redux的思想来实现状态管理
(2) 每一个reducer是一个function,这个function根据每一个action的行为不同,对数据进行操作 返回一 个新的状态数据
(3) 如果要改变数据 需要dispatch派发一个action 每一个action都有一个type属性 用来区分reducer的改变方式
7.useContext
// context上下文可以解决组件传参的问题
通过context可以定义全局数据,每个组件都可以使用,解决跨组件传参问题
export const context = createContext()
const {Provider} = context
<Provider value={{}}></Provider> // value表示数据提供者
// 使用的时候先引入
import {UseContext} from 'react'
import {context} from './Provider' 这个Provider是一个组件
const {} = useContext(context) // 解构赋值
12-自定义hooks
13-useEffect (副作用)
两个参数
-
第一个参数是一个函数,是在第一次渲染以及之后更新渲染之后会进行的副作用。
// 这个函数可能会有返回值,倘若有返回值,返回值也必须是一个函数,会在组件被销毁时执行。 // 模仿componentDidMount useEffect(() => { console.log("这里使用useEffect实现componnetDidMount效果"); // 组件销毁的时候执行,相当于componentWillUnmount return () => { console.log("componentWillUnmount"); }; }, []);
-
第二个参数是可选的,是一个数组,数组中存放的是第一个函数中使用的某些副作用属性。用来优化 useEffect
如果使用此优化,请确保该数组包含外部作用域中随时间变化且 effect 使用的任何值。 否则,您的代码将引用先前渲染中的旧值。 如果要运行 effect 并仅将其清理一次(在装载和卸载时),则可以将空数组([])作为第二个参数传递。 这告诉React你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。 // 如果依赖项发生改变,就会执行 useEffect(() => { console.log("count这里模拟componentDidUpdate"); }, [count]); useEffect(() => { console.log("name这里模拟componentDidUpdate"); }, [name]); useEffect(() => { console.log("name或者count发生改变了"); }, [count, name]);
整合了类组件componentDidMount、componentDidUpdate、componentWillUnMount等钩子函数的能力,而且代码显得更加简洁.
14-useCallback和useMemo有什么区别
useMemo对数据做缓存
useCallback对方法做缓存
定义一个方法如果不加useCallback 会造成内存浪费
// useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行
并且这两个hooks都返回缓存的值,
// useMemo返回缓存的变量,useCallback返回缓存的函数
15-什么是高阶组件/高阶函数
// 高阶组件概念:把一个组件当作参数传递给一个组件(方法),返回一个具有新的属性的组件
// 高阶函数: 把一个function函数当作参数进行传递,封装返回一个新的function,例如数组的map,reduce方法等
// react是单项数据流,数据是从属性传递到子组件,属性是只读的,不能改变
16-调用setState之后发生了什么?
// 调用setState之后会将传入的参数与当前的状态合并,也就是真实dom和虚拟dom的同步过程,就是调和过程。
// 经过调和过程,react会以高效的方式根据最新的数据状态构建一颗新的dom树,然后重新渲染页面。
// 在react构建新的dom树的过程中,会根据新旧dom的差异进行局部渲染,按需更新。
17-React中的合成事件是什么?
// 合成事件是围绕浏览器原生事件充当跨浏览器包装器的对象。
// 它们将不同浏览器的行为合并为一个 API。
// 这样做是为了确保事件在不同浏览器中显示一致的属性。
18-memo
react.memo 为高阶组件
// 它的作用是做组件的性能优化 当组件的属性信息不改变的时候组件不会重新渲染
memo是一个function 接受一个组件作为参数 返回一个新的组件
这个被memo修饰过的组件会具有新的特性,当属性数据不改变的时候组件不会被重新执行 好处就是性能优化
19-react中 key作用
react性能优化
react-router-dom
1-常用路由hooks
1.useHistory(push,replace,go)
2.useParams(获取/detail/:id这样的id参数)
3.useLocation(search,state,pathname)
redux
redux流程图
1-redux理解
// 什么?: redux是专门做状态管理的独立第3方库, 不是react插件, 但一般都用在react项目中
// 作用?: 对应用中状态进行集中式的管理(写/读)
开发: 与react-redux, redux-thunk等插件配合使用
2-redux相关API
redux中包含: createStore(), applyMiddleware(), combineReducers()
store对象: getState(), dispatch(), subscribe()
react-redux:
<Provider store={store}>: 向所有的容器组件提供store
connect(
state => ({xxx: state.xxx}),
{actionCreator1, actionCreator2}
)(UI组件):
产生的就是容器组件, 负责向UI组件传递标签属性,
一般属性值从state中获取, 函数属性内部会执行dispatch分发action
3-redux核心概念(3个)
// action:
默认是对象(同步action), {type: 'xxx', data: value}, 需要通过对应的actionCreator产生,
它的值也可以是函数(异步action), 需要引入redux-thunk才可以
// reducer
根据老的state和指定的action, 返回一个新的state
不能修改老的state
// store
redux最核心的管理对象
内部管理着: state和reducer
提供方法: getState(), dispatch(action), subscribe(listener)