http 是超文本传输协议,信息是明文传输
Https 是加密的、协议需要 ca 证书,费用较高。
https缺点:
https 握手阶段比较费时,会使页面加载时间延长 50%,增加 10%~20%的耗电。
https 缓存不如 http 高效,会增加数据开销。
SSL 证书也需要钱,功能越强大的证书费用越高。
SSL 证书需要绑定 IP,不能再同一个 ip 上绑定多个域名,ipv4 资源支持不了这种消耗。
TCP 是面向连接的,udp 是无连接的即发送数据前不需要先建立链接。
TCP 提供可靠的服务。面向连接,不会丢失数据因此适合大数据量的交换。
TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多。
TCP 是面向连接的可靠性传输,而 UDP 是不可靠的
说一下 http2.0
提升了速度
内容安全,应为 http2.0 是基于 https 的
允许多路复用:
传输信息进行二进制编码和分帧
服务器端推送
多路复用允许同时通过单一的 HTTP/2 连接发送多重请求-响应信息。改
善了:在 http1.1 中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限
制(连接数量),超过限制会被阻塞。
:HTTP2.0 会将所有的传输信息分割为更小的信息或者帧,并对他们进行二进制编码
首部压缩
状态码:
200 表示请求成功
301 永久重定向
302 临时重定向
304 get请求被允许但是,但是内容和请求条件没有改变
400 请求无效,前端提交数据的字段名称和字段类型与后台不一致,将 obj 对象通过 JSON.stringify
401 当前请求需要用户验证
403 虽然服务器已经得到请求,但是拒绝执行请求被拒绝
404 找不到资源或者接口不存在
5XX 表示服务端错误
500 服务器端的未知错误
502 网关错误
503 服务暂时无法使用
肯定会碰到: 101/200/301/302/304/400/404/502
(1)400 状态码:请求无效
产生原因:前端提交数据的字段名称和字段类型与后台的实体没有保持一致
前端提交到后台的数据应该是 json 字符串类型,但是前端没有将对象 JSON.stringify 转化成字符串。
解决方法:
对照字段的名称,保持一致性,将 obj 对象通过 JSON.stringify 实现序列化
(2)401 状态码:当前请求需要用户验证
(3)403 状态码:服务器已经得到请求,但是拒绝执行
304:如果客户端发送了一个带条件的 GET 请求且该请求已被允许,内容和请求条件并没有改变,就会返回304
双闭合标签,便于阅读,比如 nav表示导航条,类似的还有 article、header、footer 等等标签
get 参数通过 url 传递,
post 放在 request body 中。
get 请求参数会被完整保留在浏览历史记录里,而 post 中的参数不会被保留
GET 产生一个 TCP 数据包;POST 产生两个 TCP
get、post,这两个用的是最多的,还有很多比如 patch、delete、put、options 等等
XSS(跨站脚本攻击): 向网站注入JS代码,然后执行,篡改网站的内容
1. 输入过滤 限制输入框文本长度,过滤特殊字符( <> ... )
2. 在HTTP头部上配置,set-cookie
Httponly :禁止Js访问Cookie。
Secure : 仅当在https请求时发送Cookie
CSRF 跨站请求伪造 是利用网站本身的漏洞请求网站的api。
验证 HTTP Referer 字段;
在请求地址中添加 token 并验证;
在 HTTP 头中自定义属性并验
SQL注入
用户输入的文本框中不可以有特殊符号( 引号、空格 )
行内元素:span、img、input...
块级元素:div、footer、header、section、p、h1...h6...
空元素:br、hr...
元素之间的转换问题:
display: inline; 把某元素转换成了行内元素 ===> 不独占一行的,并且不能设置宽高
display: inline-block; 把某元素转换成了行内块元素 ===> 不独占一行的,可以设置宽高
display: block; 把某元素转换成了块元素 ===> 独占一行,并且可以设置宽高
HTML5新增标签:
增加了 header,footer,nav,aside,section 等语义化标签
为 input 增加了 color,emial,data ,range 等类型,
提供了 sessionStorage,localStorage
多媒体 audio 和 vedio
另外还有地理定位,canvas画布,canvas 绘图,支持SVG
css3:
border-radius,box-shadow;
background-size,background-origin;
CSS3 2D,3D 转换、transform;animation 动画
区别一:link先有,后有@import(兼容性link比@import兼容);
区别二:加载顺序差别,浏览器先加载的标签link,后加载@import
title与h1的区别:
定义:
title:概括了网站信息,可以告诉搜索引擎或者用户关于这个网站的内容主题是什么
h1:文章主题内容,告诉蜘蛛我们的网站内容最主要是什么
区别:
title他是显示在网页标题上、h1是显示在网页内容上
title比h1添加的重要 (title > h1 ) ==》对于seo的了解
场景:
网站的logo都是用h1标签包裹的
b与strong的区别:
定义:
b:实体标签,用来给文字加粗的。
strong:逻辑标签,用来加强字符语气的。
区别:
b标签只有加粗的样式,没有实际含义。
strong表示标签内字符比较重要,用以强调的。
题外话:为了符合css3的规范,b尽量少用该用strong就行了。
i与em的区别:
定义:
i:实体标签,用来做文字倾斜的。
em:是逻辑标签,用来强调文字内容的
区别:
i只是一个倾斜标签,没有实际含义。
em表示标签内字符重要,用以强调的。
场景:
i更多的用在字体图标,em术语上(医药,生物)。
区别一:
title : 鼠标移入到图片显示的值
alt : 图片无法加载时显示的值
区别二:
在seo的层面上,蜘蛛抓取不到图片的内容,所以前端在写img标签的时候为了增加seo效果要加入alt属性来描述这张图是什么内容或者关键词。
png:无损压缩,尺寸体积要比jpg/jpeg的大,适合做小图标。
jpg:采用压缩算法,有一点失真,比png体积要小,适合做中大图片。
gif:一般是做动图的。
webp:同时支持有损或者无损压缩,相同质量的图片,webp具有更小的体积。兼容性不是特别好。
CSS的盒子模型有哪些:标准盒子模型、IE盒子模型
CSS的盒子模型区别:
标准盒子模型:margin、border、padding、content
IE盒子模型 :margin、content( border + padding + content )
通过CSS如何转换盒子模型:
box-sizing: content-box; /*标准盒子模型*/
box-sizing: border-box; /*IE盒子模型*/
line-height 是每一行文字的高,如果文字换行则整个盒子高度会增大(行数*行高)。
height 是一个死值,就是这个盒子的高度。
CSS选择符:
通配(*)
id选择器(#)
类选择器(.)
标签选择器(div、p、h1...)
相邻选择器(+)
后代选择器(ul li)
子元素选择器( > )
属性选择器(a[href])
CSS属性哪些可以继承:
文字系列:font-size、color、line-height、text-align...
***不可继承属性:border、padding、margin...
优先级比较:!important > 内联样式 > id > class > 标签 > 通配
CSS权重计算:
第一:内联样式(style) 权重值:1000
第二:id选择器 权重值:100
第三:类选择器 权重值:10
第四:标签&伪元素选择器 权重值:1
第五:通配、>、+ 权重值:0
用边框画(border),例如:
{
width: 0;
height: 0;
border-left:100px solid transparent;
border-right:100px solid transparent;
border-top:100px solid transparent;
border-bottom:100px solid #ccc;
}
1. 采用 meta viewport 的方式 (缩放0.5 移动端才有效果)
3.transform: scale()
定位为上下左右为 0,margin:0 可以实现脱离文档流的居中.
margin 负值法 -height 的一半 -width 的一半
transform:translateX(-50%)和 transform:translateY(-50%)
垂直居中
设置line-height 等于height
display:flex + align-items: center
display:table+display:table-cell + vertical-align: middle;
main
.container{
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
border:5px solid #ccc;
}
.main{
background: red;
}
main
.container{
position: relative;
width: 300px;
height: 300px;
border:5px solid #ccc;
}
.main{
position: absolute;
left:50%;
top:50%;
background: red;
transform: translate(-50%,-50%);
}
main
.container{
display: grid;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
border:5px solid #ccc;
}
.main{
background: red;
}
none 隐藏元素
block 把某某元素转换成块元素
inline 把某某元素转换成内联元素
inline-block 把某某元素转换成行内块元素
BFC 是页面上的一个独立容器,子元素不会影响到外面元素
2. BFC的原则:如果一个元素具有BFC,那么内部元素再怎么弄,都不会影响到外面的元素。
3. 如何触发BFC:
float的值非none
overflow的值非visible
display的值为:inline-block、table-cell...
position的值为:absoute、fixed
1. 触发BFC
2. 多创建一个盒子,添加样式:clear: both;
3. after方式
ul:after{
content: '';
display: block;
clear: both;
}
偶数 : 让文字在浏览器上表现更好看。
另外说明:ui给前端一般设计图都是偶数的,这样不管是布局也好,转换px也好,方便一点。
static [默认] 没有定位
fixed 固定定位,相对于浏览器窗口进行定位。
relative 相对于自身定位,不脱离文档流。
absolute 相对于第一个有relative的父元素,脱离文档流。
relative和absolute区别
1. relative不脱离文档流 、absolute脱离文档流
2. relative相对于自身 、 absolute相对于第一个有relative的父元素
3. relative如果有left、right、top、bottom ==》left、top
absolute如果有left、right、top、bottom ==》left、right、top、bottom
双飞翼:
left、center、right三种都设置左浮动
设置center宽度为100%
设置负边距,left设置负边距为100%,right设置负边距为自身宽度
设置content的margin值为左右两个侧栏留出空间,margin值大小为left和right宽度
reset.css 是一个css文件,用来重置css样式的。
normalize.css 为了增强跨浏览器渲染的一致性,一个CSS 重置样式库。
1. 是什么
把多个小图标合并成一张大图片。
2. 优缺点
优点:减少了http请求的次数,提升了性能。
缺点:维护比较差(例如图片位置进行修改或者内容宽高修改)
1. 占用位置的区别
display: none; 是不占用位置的
visibility: hidden; 虽然隐藏了,但是占用位置
2. 重绘和回流的问题
display: none; 页面产生重绘+回流
visibility: hidden; 产生重绘
display: none 会影响计数器
visibility: hidden 不会影响计数器,且具有继承性,比如父盒子设置了visibility: hidden,子元素会继承该属性,当子元素再次设置 visibility: visible,元素又会显示。
产生回流一定会造成重绘,但是重绘不一定会造成回流。
产生回流的情况:改变元素的位置(left、top...)、显示隐藏元素....
产生重绘的情况:样式改变、换皮肤
什么是重绘?
样式改变不影响文档流时,
background、boder-color、visibility
等。
什么是回流?
元素的
尺寸、布局、隐藏
等改变时候、造成浏览器重新渲染部分DOM或全部DOM的过程。
在css中:
在dom的最末端改变class
避免设置多层内联样式
避免使用table布局
使用 transfrom、opacity、filter等动画效果不会造成重绘问题
在js中:
避免一个样式修改完接着下一个样式 (应该一次性改好,或者定义class类)
避免重复操作dom,(使用文档创建子树,然后拷贝到文档中)
复杂动画使用绝对定位让其脱离文档流,否则会引起父元素和后续元素大量回流。
共同性:实现透明效果
1. opacity 取值范围0到1之间,0表示完全透明,1表示不透明
2. rgba R表示红色,G表示绿色,B表示蓝色,取值可以在正整数或者百分数。A表示透明度取值0到1之间
区别:继承的区别
opacity会继承父元素的opacity属性,而RGBA设置的元素的后代元素不会继承不透明属性。
延迟加载:async、defer
例如:
defer : 等html全部解析完成,才会执行js代码,顺次执行js脚本。
async : async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)。
基本类型:string、number、boolean、undefined、null、symbol、bigint
引用类型:object (arr、fun是引用类型的小类,也是object)
NaN是一个数值类型,但是不是一个具体的数字。
布尔值和num相加 布尔会转换成 1/2
字符串和其他类型相加 另一个都会变为字符串类型
typeof(null) 是一个特殊的对象类型
console.log( true + 1 ); //2
console.log( 'name'+true ); //nametrue
console.log( undefined + 1 ); //NaN
console.log( typeof undefined ); //undefined
console.log( typeof(NaN) ); //number
console.log( typeof(null) ); //object
1. 作者在设计js的都是先设计的null(为什么设计了null:最初设计js的时候借鉴了java的语言)
2. null会被隐式转换成0,很不容易发现错误。
3. 先有null后有undefined,出来undefined是为了填补之前的坑。
具体区别:JavaScript的最初版本是这样区分的:null是一个表示"无"的对象(空对象指针),转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。
== : 比较的是值
string == number || boolean || number ....都会隐式转换
通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。)
=== : 除了比较值,还比较类型
就是异步的事件队列里面分为宏任务和微任务。
1. js是单线程的语言。
2. js代码执行流程:同步执行完==》事件循环
同步的任务都执行完了,才会执行事件循环的内容
进入事件循环:请求、定时器、事件....
3. 事件循环中包含:【微任务、宏任务】
宏任务:setTimeout、异步ajax请求、文件操作等
微任务:Promise.then、.catch 和 .finally
要执行宏任务的前提是清空了所有的微任务
流程:先执行同步的==》再执行事件循环里的微任务和宏任务 ==》微任务==》宏任务=》微任务...
1. 除了函数外,js是没有块级作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
(function(){
var a = b = 10
})()
console.log(a) // 报错
console.log(b) // 10 因为b没有被var定义,是全局变量(window.b)
4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
面试的时候怎么看:
1. 本层作用域有没有此变量【注意变量提升】
2. 注意:js除了函数外没有块级作用域
3. 普通声明函数时不看写函数的时候顺序
function c(){
var b = 1;
function a(){
console.log( b );
var b = 2;
console.log( b );
}
a();
console.log( b );
}
c();
// undefind 2 1 注意变量提升
var name = 'a';
(function(){
if( typeof name == 'undefined' ){
var name = 'b';
console.log('111'+name);
}else{
console.log('222'+name);
}
})()
// 222a 错误 js除了函数外没有块级作用域,fun 里面有定义b
// 正确应该是 111b
function fun( a ){
var a = 10;
function a(){}
console.log( a );
}
fun( 100 ); // 10
// 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
JS对象注意点:
1. 对象是通过new操作符构建出来的,所以对象之间不相等(除了引用外);
2. 对象注意:引用类型(共同一个地址);
3. 对象的key都是字符串类型;
4. 对象如何找属性|方法;
查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
[1,2,3] === [1,2,3] //false
var obj1 = {
a:'hellow'
}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); //{a:world}
(function(){
console.log(a); //undefined
var a = 1;
})();
var a = {}
var b = {
key:'a'
}
var c = {
key:'c'
}
a[b] = '123'; //key 不应该是一个对象,如果是对象会被转换成字符串'object Object '
a[c] = '456'; // 替换[b]
console.log( a[b] ); // 456
var a = {};
var b = { key: "a" };
var c = { key: "c" };
//b要作为属性名先转为字符串,相当于:a[object Object]:="123"
a[b] = "123";
//同上,c要作为属性名先转为字符串,相当于:a[object Object]:="456"
a[c] = "456";
//上面的a[c] = "456"只是给a[object Object]重新赋值而已。
//打印a[b]其实就是在打印a[object Object],由于他的最一次的赋值为456。
//故:
console.log(a[b]);//456
因为key名称只能是字符串,b/c单做键会调用toString得到的都是[object Object],a[b],a[c]都等价于a["[object Object]"],所以后面的赋值会覆盖前一次操作,打印的时候a[b]也是同理,那不就是更新[object Object]这个键的值了。
function Foo(){
getName = function(){console.log(1)} //注意是全局的window.
return this;
}
Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
console.log(5)
}
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo().getName();//3
var o = {
a:10,
b:{
a:2,
fn:function(){
console.log( this.a ); // 2
console.log( this ); //代表b对象
}
}
}
o.b.fn(); // 2 {a:2,fn{}}
window.name = 'ByteDance';
function A(){
this.name = 123;
}
A.prototype.getA = function(){
console.log( this );
return this.name + 1;
}
let a = new A();
let funcA = a.getA;
funcA(); //this代表window
var length = 10;
function fn(){
return this.length + 1;
}
var obj = {
length:5,
test1:function(){
return fn();
}
}
obj.test2 = fn;
console.log( obj.test1() ); //1
console.log( fn()===obj.test2() ); //false
console.log( obj.test1() == obj.test2() ); //false
var arr = [1,2,3];
console.log( Array.isArray( arr ) );
var arr = [1,2,3];
console.log( arr instanceof Array );
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );
var arr = [1,2,3];
console.log( Array.prototype.isPrototypeOf(arr) )
var arr = [1,2,3];
console.log( arr.constructor.toString().indexOf('Array') > -1 )
1. slice是来截取的
参数可以写slice(3)、slice(1,3)、slice(-3)
返回的是一个新的数组
2. splice 功能有:插入、删除、替换
返回:删除的元素
该方法会改变原数组
arr.sort((n1,n2)=>n1-n2) //正序排序
arr.sort((n1,n2)=>n2-n1) // 降序排序
Math.max(...arr) // 求最大值
Math.min(...arr) // 求最小值
arr.splice(9, 1);删除后数组中索引9的元素 并返回删除的值
arr.splice(arr.findIndex((e) => e.id === 1), 1) //数组包含对象
arr.splice(arr.findIndex((e,index) => index === 2), 1) //返回的是删除后的数组
var arr1 = [1,2,3,2,4,1];
// new set返回的是一个对象,把它转换成数组,两种方法
console.log(Array.from(new Set(arr1)));
function unique(arr){
return [...new Set(arr)]
}
console.log(unique(arr1) );
var arr2 = [1,2,3,2,4,1];
function unique( arr ){
var brr = [];
for( var i=0;i<arr.length;i++){
// 等于-1说明没找到
if( brr.indexOf(arr[i]) == -1 ){
brr.push( arr[i] );
}
}
return brr;
}
console.log( unique(arr2) );
var arr3 = [1,2,3,2,4,1];
function unique( arr ){
arr = arr.sort();
var brr = [];
for(var i=0;i<arr.length;i++){
if( arr[i] !== arr[i-1]){
brr.push( arr[i] );
}
}
return brr;
}
console.log( unique(arr3) );
function fnArr(arr){
var newArr = [];
arr.forEach((item,index)=>{
newArr.push( Math.max(...item) )
})
return newArr;
}
console.log(fnArr([
[4,5,1,3],
[13,27,18,26],
[32,35,37,39],
[1000,1001,857,1]
]));
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld
解答:
String.prototype.addPrefix = function(str){
return str + this;
}
console.log( 'world'.addPrefix('hello') )
找出字符串出现次数返回一个对象:
var str = 'abccbaam'
function funique(){
let obj = {};
for(let i = 0; i<str.length; i++){
obj[str[i]] ? obj[str[i]] += 1 : obj[str[i]] = 1
}
return obj;
}
console.log(funique(str));
找出字符串出现最多次数的字符以及次数
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){
var char = str.charAt(i); //str[i]
if( obj[char] ){
obj[char]++;
}else{
obj[char] = 1;
}
}
console.log( obj );
//统计出来最大值
var max = 0;
for( var key in obj ){
if( max < obj[key] ){
max = obj[key];
}
}
//拿最大值去对比
for( var key in obj ){
if( obj[key] == max ){
console.log('最多的字符是'+key);
console.log('出现的次数是'+max);
}
}
function querUrlparms(url) {
let obj = {}
if (url.indexOf('?') < 0) return obj
let arr = url.split('?')[1].split('&')
for (let i = 0; i < arr.length; i++) {
const arr2 = arr[i].split('=');
obj[arr2[0]] = arr2[1] // 组合成键值对的形式
}
return obj
}
console.log(querUrlparms('https://www.baidu.com/s?wd=js&rsv_spt=1&rsv_iqid=0xfa41b9e2000013a3&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=4&rsv_sug2=0&rsv_btype=i&inputT=397&rsv_sug4=855'));
使用 正则+ …arg
function queryURLparamsRegEs6(url) {
let obj = {}
let reg = /([^?=&]+)=([^?=&]+)/g
url.replace(reg, (...arg) => {
obj[arg[1]] = arg[2]
})
return obj
}
1. 创建了一个空的对象
2. 将空对象的原型,指向于构造函数的原型(原型赋值)
3. 将空对象作为构造函数的上下文(改变this指向,指向于新的对象)
4. 对构造函数有返回值的处理判断 (如果构造函数返回基本类型,则构造函数的return无效,返回对象则new操作符无效)
function Fun( age,name ){
this.age = age;
this.name = name;
}
function create( fn , ...args ){
//1. 创建了一个空的对象
var obj = {}; //var obj = Object.create({})
//2. 将空对象的原型,指向于构造函数的原型
Object.setPrototypeOf(obj,fn.prototype);
//3. 将空对象作为构造函数的上下文(改变this指向)
var result = fn.apply(obj,args);
//4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三') )
普通一个函数,执行完后,函数以及内部的变量就被销毁了, 如果在这个函数里面又新建了一个函数,然后在普通函数内调用了外部函数的变量,此时就形成了一个闭包,当函数执行完毕,外部函数不被销毁,造成了变量驻留在内存中,实现了无意的环境共享。
1. 闭包是什么
闭包的实质是因为函数嵌套而形成的作用域链
闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
2. 闭包可以解决什么问题【闭包的优点】
2.1 内部函数可以访问到外部函数的局部变量
2.2 闭包可以解决的问题
var lis = document.getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
alert(i);
}
})(i)
}
3. 闭包的缺点
3.1 变量会驻留在内存中,造成内存损耗问题。
解决:把闭包的函数设置为null
3.2 内存泄漏【ie】 ==> 可说可不说,如果说一定要提到ie
1. 什么是原型? 原型可以解决什么问题
实现 对象属性和方法共享
2. 谁有原型
函数拥有:prototype
对象拥有:__proto__
3. 原型链是什么?
就是把原型串联起来(对象的属性和方法可以在对象本身也可以放在原型中)
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4.2 原型链的最顶端是null(查找到null会返回undefined)
原型链的数据结构:
链表结构,通过__proto__指针把每个原型串联起来。
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
function Fun(){
//this.run = '1' -> 2.构造函数中查找
}
//Fun.prototype.run = '2' -> 4.构造函数的原型中
var obj = new Fun();
//obj.run = '3'; -> 1.先在对象本身查找
//obj.__proto__.run = '4' -> 3. 对象的原型
Object.prototype.run = '5'; // -> 5. 当前原型的原型中查找
console.log( obj.run )
// console.log( Fun.prototype , obj.__proto__ )
// console.log( obj.__proto__.__proto__.__proto__ ); // null
class Parent{
constructor(){
this.age = 18;
}
}
class Child extends Parent{
constructor(){
super(); //虽然语法没问题,但是不加super(),浏览器会报错
this.name = '张三';
}
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );
这里会出现一个问题,类似于浅拷贝,是地址的引用,所有属性和方法都来自于原型,所有new 出来的对象都共用一个方法或属性,改变一个属性值会互相影响.
function Parent(){
this.age = 20;
}
function Child(){
this.name = '张三'
}
Child.prototype = new Parent();
let o2 = new Child();
console.log( o2,o2.name,o2.age );
所有属性和方法都是独立的。
function Parent(){
this.age = 22;
}
function Child(){
this.name = '张三'
Parent.call(this);
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );
提取了原型继承和借用构造函数继承的优点。不共享的用call,共享的则指向原型。
function Parent(){
this.age = 100;
}
function Child(){
Parent.call(this);
this.name = '张三'
}
Child.prototype = new Parent();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Child.prototype.constructor = Son;
let o4 = new Child();
console.log( o4,o4.name,o4.age );
可以改变this指向
语法: 函数.call()、函数.apply()、函数.bind()
1. call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
2. 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。
1. 用apply的情况
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1) )
2. 用bind的情况
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
console.log( this.id );
}.bind(h1s)
V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用 InsertionSort,比10大的数组则使用 QuickSort。
之前的版本是:插入排序和快排,现在是冒泡
原理实现链接:https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js
***710行代码开始***
共同点:复制
1. 浅拷贝:只复制引用,而未复制真正的值。
var arr1 = ['a','b','c','d'];
var arr2 = arr1;
var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);
2. 深拷贝:是复制真正的值 (不同引用)
var obj3 = {
a:1,
b:2
}
var obj4 = JSON.parse(JSON.stringify( obj3 ));
//递归的形式
function copyObj( obj ){
Array.isArray(obj) ? var newObj = [] : var newObj = {}
for( var key in obj ){
if( typeof obj[key] == 'object' ){
newObj[key] = copyObj(obj[key]);
}else{
newObj[key] = obj[key];
}
}
return newObj;
}
console.log( copyObj(obj5) );
公共点:在客户端存放数据
区别:
1. 数据存放有效期
sessionStorage : 会话存储,当前窗口关闭后就会删除。
localStorage : 持久化存储,始终有效,窗口或者浏览器关闭也一直保存。
cookie : 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2. localStorage、sessionStorage不可以设置过期时间
cookie 有过期时间,可以设置过期,使用时需要环境(把时间调整到之前的时间,就过期了)
3. 存储大小的限制
cookie存储量不能超过4k
localStorage、sessionStorage不能超过5M
****根据不同的浏览器存储的大小是不同的。
1. 易读性和维护性更好。
2. seo成分会更好,蜘蛛抓取更好。
3. IE8不兼容HTML5标签的。解决:可以通过html5shiv.js去处理。
1. 区别
:是伪类、::伪元素 ===》是为了做区分
2.是什么?作用
元素before之前 、 元素after之后
作用:清除浮动、样式布局上也有作用
Chrome默认字体大小是:16px
**每个浏览器默认字体大小可能都不一样
相对于font-size
em针对于父元素的font-size
rem针对于根(html)元素的font-size
淘宝无限适配【移动端】:淘宝无限适配 + 布局单位使用rem
1. 是什么?
一个URL可以响应多端
2. 语法结构
@media only screen and (max-width: 1000px){
ul li:last-child{
display: none;
}
}
only : 可以排除不支持媒体查询的浏览器
screen : 设备类型
max-width | max-height
min-width | min-height
3. 响应式图片【性能优化】
布局方案
一、什么情况下采用响应式布局
数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式布局
例如:公司的官网、专题页面
特别追求性能的项目,不太适合响应式,因为如果添加了很多的响应式就会造成加载速度变慢。
二、pc + 移动端应该做什么样的布局方案
注意:访问量还可以或者比较大,类似于淘宝网。
pc是一套,会加入一点点响应式。
移动端是一套,会使用自适应的布局方式。
三、pc的设计图
ui:1980
笔记本电脑:1280
ui图的宽度和电脑的宽度不对应该怎么办?
1. 把ui图进行等比缩放,缩放成和电脑一样的尺寸
2. 换1980的电脑
四、移动端的设计图
宽度:750
因为750设计图/2就是375,正好是iphone6的尺寸,我们要把iphone6的尺寸做为基准点。
Normalize.css 跨浏览器端样式重置,所有浏览器显示一致。
为什么存在300ms延迟?
因为移动端双击屏幕默认会有放大效果,所有存在300ms延迟 (等待点击第二次)。
在移动端使用click事件有300ms延迟的问题 解决方式:
禁止双击缩放
// html头部 meta 标签加入:
mate:user-scalable=no
使用插件 fastclick.js
// 引入js文件写入代码:
if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); }
移动端的事件 (touch)
touchstart
:开始触摸事件
touchmove
: 手指滑动事件
touchend
: 触摸结束事件
解决方式:
1、阻止默认行为 e.preventDefault()
;
2、fastclick.js
// 引入js文件 2.2 写入代码
if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false);
"text" autocapitalize='off'>
具体就是,new Date('2020-11-12 00:00:00') 在ios中会为NAN
解决方案:用new Date('2020/11/12 00:00:00') 的日期格式,或者写个正则转换
禁止ios 长按时触发系统的菜单,禁止ios&android长按时下载图片
html,body{
touch-callout: none;
-webkit-touch-callout: none;
user-select:none;
-webkit-user-select:none;
}
html,body{
user-select:none;
-webkit-user-select:none;
}
input{
line-height:normal;
}
ES6用过那些:
let const
解构赋值、模板字符串、箭头函数、promise、async await 、class、函数默认值
拓展运算符(...name)
map&set数据结构、模块化语法
数组方法:
map 如果想要返回一个数组
reduce 如果需要一个结果,比如累加求和
find 找到后立即返回满足条件的第一个值
findindex 返回索引
map&set数据结构 map有key和value set只有value(不允许值重复)
filter 过滤一个结果是否存在(可以为空,所以需要判断) Es5
var、let、const 共同点都是可以声明变量的
区别一:
var 具有变量提升的机制
let和const没有变量提升的机制
区别二:
var 可以多次声明同一个变量
let和const不可以多次声明同一个变量
区别三:
var、let声明变量的
const声明常量
var和let声明的变量可以再次赋值,但是const不可以再次赋值了。
区别四:
var声明的变量没有自身作用域
let和const声明的变量有自身的作用域
考题一:let和const没有变量提升性
console.log( str );//undefined
var str = '你好';
console.log( num );//报错
let num = 10;
考题二:
function demo(){
var n = 2;
if( true ){
var n = 1;
}
console.log( n );//1
}
demo();
function demo(){
let n = 2;
if( true ){
let n = 1;
}
console.log( n );//2
}
demo();
考题三:可以修改
const obj = {
a:1
}
obj.a = 11111;
console.log( obj )
const arr = ['a','b','c'];
arr[0]= 'aaaaa';
console.log( arr );
方式一:Object.assign
const a = {a:1,b:4};
const b = {b:2,c:3};
let obj1 = Object.assign(a,b);
console.log( obj1 );
方式二:…
let obj2 = {...a,...b};
console.log( obj2 );
方式三:自己封装方法
function extend( target, source ){
for(var key in source){
target[key] = source[key];
}
return target;
}
console.log( extend(a,b) );
1. this指向的问题
箭头函数中的this只在箭头函数定义时就决定的,
箭头函数继承来的this指向不会改变(call、apply、bind无效)
箭头函数不能作为构造函数
没有arguments、prototype
(call、apply、bind)
****箭头函数的this定义时候、指向外层第一个普通函数的this
2. 箭头函数不能new(不能当作构造函数)
3. 箭头函数prototype
4. 箭头函数arguments
有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)
区别一:返回的内容不同
filter 返回是新数组
find 返回具体的内容
区别二:
find :匹配到第一个即返回
filter : 返回整体(没一个匹配到的都返回)
some ==》 如果有一项匹配则返回true
every ==》 全部匹配才会返回true
for..in 用于循环可迭代对象,虽然可以用于字符串和数组,但不建议。
for...of 用于用于循环字符串或数组
SPA是什么?单页面应用
缺点:
1. SEO优化不好
2. 性能不是特别好
3. 首次加载慢
多页面SEO比单页面SEO好 (方便蜘蛛抓取),收入得多。
把一个大的网页拆分成多个组件。
在vue中我们通过插值表达式渲染页面数据,M就是这个data(model层),v就是这个展示的数据即(view视图层)。
MV 就是数据展示到页面过程,这个时候是单向的
VM就是理解为v-model原理实现,通过视图去更新数据
什么是响应式数据? 视图发生修改data中的数据也发生修改。
vue数据双向绑定原理通过`数据劫持`结合`发布订阅模式`的方式来实现的。
核心就是使用 Object.defineProperty()劫持数据 的 get/set 监听数据的变化
首先我们要有一个`数据监听器`,用来劫持并监听所有属性,如果改变,就通知订阅者Watcher。
然后还需要一个`指令解析器`,她用于节点遍历和重新渲染以及绑定相应的更新函数。
还要实现一个`订阅者Watcher`,作为数据监听器和指令解析器的桥梁,数据变化时执行相应函数,从而更新视图。
在使用vue时候,明显能体验到一个点: 数据是在data中定义的,然后能直接通过this.xx获取数据,而且一个修改数据就会互相通知跟着一起改变。所以这里一定不是通过直接赋值和深浅拷贝实现的,而通知的实现就是通过Object.defineProperty()里的get和set方法,监控是获取还是修改。
其实就是数据,把dom变成数据结构。
优点:
响应式、组件化、插件化、
响应式 MVVM 一个大网页可以拆分成多个组件合并成一个大的,利于维护。
只要整理好数据结构,通过数据驱动DOM
缺点:
原理还是DOM操作,只是vue有数据驱动, 初次加载耗时多,而且可能会造成重绘,性能没前后端不分离好。
由于是单页面应用,不能使用浏览器前进后退功能,所有页面切换切换需要自己建立管理栈。
因为是数据驱动,所以页面源码中很少有内容,所以不利于seo。
1. 展示形式不同
v-if是 创建一个dom节点
v-show 是display:none 、 block
2. 使用场景不同
初次加载v-if要比v-show好,页面不会做加载盒子
频繁切换v-show要比v-if好,创建和删除的开销太大了,显示和隐藏开销较小
v-for的优先级要比v-if高
***是在源码中体现的:function genElement
来获取dom的
获取更新后的dom内容
1. 作用:让样式在本组件中生效,不影响其他组件。
2. 原理:给节点新增自定义属性,然后css根据属性选择器添加样式。
stylus样式穿透使用:>>>
sass和less使用:/deep/
通用使用: :v-deep
父组件-->子组件:
1. 父组件:
export default {
components: {
UserDetail
}
......
}
2. 在子组件中使用props(可以是数组也可以是对象)接收即可。可以传多个属性。
export default {
props: ['myName']
}
子组件-->父组件:
1. 子组件
export default {
methods: {
//子组件的事件
changeParentName: function() {
this.$emit('handleChange', 'Jack')
}
}
}
2. 父组件
methods: {
changeName(name) {
this.name = name
}
}
兄弟组件之间:bus.js
props ===> methods ===> data ===> computed ===>watch
相同点: 都可以对data数据进行二次修改。
执行问题:computed有缓存,只会执行一次,以后都会走缓存,methods无缓存,所以每次调用都会重新执行。
computed
计算属性methods
存放方法的watch
监听数据变化1. computed 、methods区别
如果在值不修改的情况下,computed有缓存,methods无缓存
computed应用场景: 例如计算价格时需要在页面多次展示,它执行一次,剩下n次走缓存。而使用methods会被执行多次。
2. computed vs watch区别
watch 在不做任何的配置,数据不改变的情况下,进入组件是不会直接执行的。
watch可以深度监听,可以监听数据、路由的变化 数据或者路由发生了改变才可以响应(执行)
computed计算某一个属性的改变,如果某一个值改变了,计算属性会监听到进行返回,
如果涉及到多值可能修改的情况,使用computed是最好的,watch是需要监听多个值太麻烦了。
<template>
<div >
{{ obj }}
<button @click="btnShow">点击更新obj</button>
</div>
</template>
<script>
export default {
data() {
return {
obj: { a: '你好啊',b: '2022'}
}
},
methods: {
btnShow() {
// this.obj.c = 'fsdfsd' //这样写发现点击无法更新页面数据
this.$set(this.o, 'c', 'liaoyi') // 使用$set 可以完美解决 (点击就可以更新了)
}
}
}
</script>
生命周期:组件从创建到销毁的一个过程,4个阶段:创建、挂载、更新、销毁。
vue的功能都是围绕生命周期进行的,在周期不同的阶段调用对应的钩子函数,实现组件数据的管理和DOM的渲染
4461
1. 有哪些生命周期
系统自带:
创建前后:
beforeCreate → 可以加loading事件
created → 发起异步请求、结束loading事件
挂载前后:
beforeMount
mounted → 操作DOM、发送请求
更新前后:
beforeUpdate → 可以在这里访问dom,删除一些事件监听器
updated
销毁前后:
beforeDestroy 销毁前,删除一些提示
destroyed
2. 一旦进入到页面或者组件,会执行哪些生命周期,顺序。
beforeCreate、created、beforeMount、mounted
3. 在哪个阶段有$el,在哪个阶段有$data
beforeCreate 啥也没有
created 有data没有el
beforeMount 有data没有el
mounted 都有
4. 如果加入了keep-alive会多俩个生命周期
activated、deactivated
5. 如果加入了keep-alive,第一次进入组件会执行哪些生命?
第一次:
beforeCreate
created
beforeMount
mounted
activated
6. 如果加入了keep-alive,第二次或者第N次进入组件会执行哪些生命周期?
只执行一个生命周期: 第二次或第N次
activated
项目中使用最多的就是 created
、mounted
created ==》 请求接口 (因为有data没有el,mounted会造成重绘或回流)
mounted ==》 获取DOM(做效果,获取dom的高度,用插件)
其他问题:链接:https://pan.baidu.com/s/1Uo-jUGyxBlYDwjRadtnBWQ 密码:tr5c
1. 是什么
vue系统自带的一个组件,功能:是来缓存组件的。===》提升性能
2. 使用场景
就是来缓存组件,提升项目的性能。具体实现比如:首页进入到详情页,如果用户在首页每次点击都是相同的,那么详情页就没必要请求N次了,直接缓存起来就可以了,当然如果点击的不是同一个,那么就直接请求。
路由模式:
history http://localhost:8080/mine
hash http://localhost:8080#/mine
1. 样式不同
2. 请求
如果在找不到对应这个路由的情况下 (比如在链接后面随便输入了东西, 解决:可以配置404页面)
history会发送一次get请求、hash不会发送
3.打包后前端环境自测
history无内容(空白页),hash有内容。
push 跳转并新增一条历史记录
replace 替换掉当前页面,并替换掉当前的历史记录
go(n) 浏览历史中前进和后退
back() 后退到上一个页面
forward() 前进到下一个页面
有两种传值方式: 一个是通过query显示传值, 一个是通过参数params隐式传值。
因为使用隐式传值跳转刷新界面后参数问题无法解决,所以路径传值一定是显示的。
1. 显式 ==> 参数在url上
http://localhost:8080/about?a=1
传:this.$router.push({
path:'/about',
query:{
a:1
}
})
接:this.$route.query.a
2. 隐式 ==> 参数不在url上
http://localhost:8080/about
传:this.$router.push({
name:'About', //传name
params:{
a:1
}
})
接:this.$route.params.a
路由导航守卫
全局、路由独享、组件内
1. 全局
beforeEach、beforeResolve、afterEach
2. 路由独享
beforeEnter 在路由vue-router配置的路由单独设置 beforeEnter(){}
3. 组件内 - 不好维护,一般不用
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
使用场景:判断是否登录,如果登录就next否则就跳转到登录页面
子路由: 好维护,不用一直新建一级路由
动态路由: 值路由很多(999+),这时候使用动态路由方便
比如商城的商品点击详情页数据的渲染 -> 首页传id,详情页接收id,在请求id
使用动态路由: http://localhost:8080/detail/123323?*** 首页传id,
不使用动态路由: http://localhost:8080/detail
什么时候用vuex?
1. 传值复杂
2. 数据多,需要更好的去管理数据(方便,项目好维护)
什么时候用 modules?
modules是为了把项目再次细分,方便后期维护
【例如:商城项目有很多state】, 购物车,我的,订单管理,地址管理...为了更好的维护和管理,采用modules
state、getters、mutations、actions、modules
state 存放数据,类似于组件的data
getters 计算属性,类似于组件的computed
mutations 存放方法,类似于组件的methods
actions 存放方法,用于提交mutations,不建议是直接在actions中写入方法,actions可以包含异步操作(这个是mutaitons做不了的)
modules 把以上4个属性再细分,让仓库更好管理
Vuex是单向数据流 不能直接修改vuex的数据,只能在mutaitons中更改。
因为是vuex是单向数据流
mutaitons : 必须是同步函数
actions : 可以包含任意异步操作
使用的时候,方法就写在mutations中(逻辑)。
actions是来提交mutaitons的,不建议直接在actions里面写
如果在组件修改了vuex的数据,刷新页面为什么会到之前没有修改过的值?
因为Vuex本身不是持久化存储:
1. 使用localStorage、 自己写
cookie 但是cookie需要环境,指向域名或ip地址。
2. 使用vuex-persist 插件( 这个插件的原理也是localStorage)
vuex 存储的数据虽然是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
vue打包成多页面使用插件 : prerender-spa-plugin
项目打包时遇到的问题:
解决:1. 打包后路径问题 : (run build)默认在电脑的根目录查找
module.exports = {
publicPath:'./', //改为当前根目录
}
2. 内容不显示【路由问题】路由切换无法加载
这时候前端想要正常切换页面,路由模式修改成hash模式即可(#)
【后端】打包后如果要是history模式,需要后端做重定向
3. 如果是代理请求的数据,打包后不生效
vue中的代理是来解决跨域的问题的!
注意:代理在生产环境不生效(axios的url写的是什么,就是什么,不用代理的前缀)
在项目根目录 新建 .env.development
、.env.production
配置文件
.env.development ==>开发环境
# 页面标题
VUE_APP_TITLE = 开发环境
# 开发环境配置
VUE_APP_ENV = 'development'
# 开发环境代理
VUE_APP_BASE_API = 'http://localhost:3000'
.env.production ===>生产环境
# 页面标题
VUE_APP_TITLE = 生产环境
# 生产环境配置
VUE_APP_ENV = 'production'
# 生产环境代理
VUE_APP_BASE_API = 'http://localhost:3000'
开发和打包的时候会vue自动找这个配置文件。
一、搭建后端目录
全局命令:npm install express-generator -g
进入项目目录:express --view=ejs server
二、node — express 跨域
router.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Methods', '*');
res.header('Content-Type', 'application/json;charset=utf-8');
next();
});
app.json进行配置
"window":{
"navigationStyle":"custom",
}
工具==》详情==》本地设置==》不校验合法域名 : 项目上线前URL一定要请求到(不勾选也可以请求到数据)
应用生命周期、页面生命周期、组件生命周期
在工具中,打if出现的条件编译
例如:
这是h5端
1. http请求
能不能减少(能不能合并)
2. 图片的雪碧图
3. script标签位置
4. link标签(css引入)
1. 图片懒加载
2. 响应式图片 (如果是开发响应式网站)
3. webp代替其他格式
4. 小图标可以改用字体图标
1. 减少重绘和回流
2. 改变位置使用transform
3. 动画尽量用requestAnimationFrame,不要用定时器
计算首屏加载时间:
(performance.timing,domComplete - performance.timing.navigationStart) / 1000
1. 长列表 (1000条数据,如何展示到页面上)
2. 项目的html文件、css文件、图片、js文件压缩打包
1. keep-alive 缓存组件
2. 路由懒加载
3. 方法的正确使用
v-if和v-show
computed、watch、methods
v-for循环的时候,尽量别用index,而是用id
4. Object.freeze :冻结对象
纯展示类的接口数据,冻结就可以了
5. 使用ui组件按需引入
webpack是一个模块打包工具,用于管理项目模块依赖,并编译出所需的静态资源。
功能:
代码转换: ts编译成js,scss编译成css
文件优化: 压缩代码、图片
代码分割: 提取多页面的公共代码,比如提取首页不需要执行部分的代码让它异步加载。
代码校验、自动发布。
file/ css/ style / less / ts / babel-loader
mini-css-extaact-plugin 提取css为单独文件
clean-webpack-plugin 目录清理
copy-webpack-plugin 拷贝文件
webpack-bundle-analyzer 可视化输出打包后各文件大小
分布式代码托管平台
分布式:大家可以提交代码, (本地也有暂存区,即使断网了也可以运行git add . 和 git commit先提交到本地)。
git init 初始化仓库(生成.git文件夹--隐藏文件夹)
git config -l 查看配置 (配置的用户名,邮箱、可以用来追溯是谁提交的代码)
git status 查看状态 (标记为红色说明文件没有被git管理,需要把文件添加到暂存区,绿色说明已被管理)
git add . 把当前项目所有文件添加到暂存区 (让git接管当前项目的文件)
git add index.js 指定文件上传到暂存区
git diff 查看修改的文件
git log 查看修改历史(详细输出,谁修改的,几点修改,以及提交时间)
git reflog 查看修改历史(简单输出,只输出提交了几次和修改的文件)
回到之前的版本:
git reset --hard HEAD^
git reset --hard 版本号 回到指定版本 ( 执行git reflog 最开头的[f781dee] 就是版本号)
git commit -m '备注内容' 添加到分支中 (默认添加到master主分支)
git push 推送到云端
分支的操作:
***默认的主分支 : master
git branch (查看本地分支)
git branch 分支名称 (新建分支)
git checkout 分支名称 (切换分支)
如何查看远程仓库的分支
git branch -r
git push --set-upstream origin 分支名称 (本地分支提交到远程仓库)
git branch -d 分支名称 (删除本地分支)
如何删除远程仓库的分支
git push origin --delete 分支名称
如果远程仓库有人更新了代码,那我们不能直接 git push
解决: 必须先拉取远程仓库的代码(执行 git pull ),然后再push
但是,有的时候还是会出现问题,多人同时修改了一个文件,导致代码冲突,后一个人提交时报错:
后提交的人解决问题:手动解决冲突代码,协商保留或者删除。
工作中应该写一个模块就提交,避免自己碰到代码冲突!。
前端:会碰到jsonp跨域问题、vue的项目可以设置代理(打包后无效。解决:.ENV文件)
后端:CORS
1. 端口不同
http :80端口
https :443端口
2. https比http更加安全
***https就是证书
token是后端生成的
token + cookie : 前端判断是否过期
token + localStorage : 后端判断是否过期给前端返回code码,前端判断code码等于多少
1. 网站一定要多页面
2. title、描述、关键字
3. 图片、音频、视频、的标签属性特别关键
4. 网站不能出现死链接
nanoid库和uuid库一样都可以生成uuid,但是nanoid相比uuid要更轻量级。
npm i nanoid -S
import {nanoid} from 'nanoid'
直接调用nanoid(),即可生成一个uuid
图片懒加载,添加自定义指令。
cnpm i vue-lazyload -S
import VueLazyload from 'vue-lazyload'
// 传入一个配置对象,设置懒加载默认的 loading图片
Vue.use(VueLazyload, {
loading: require('@/assets/images/loding.gif'),
})
在组件中使用:
:src 替换成 v-lazy 即可
下载:高版本问题太多,建议下载指定版本
cnpm i vee-validate@2 -S
避免 main.js 杂乱,我们在src目录新建
plugins/validate.js
文件
在mian.js引入:
import "@/plugins/validate"; //引入表单校验插件
编写 validate.js 文件:
import Vue from "vue";
import VeeValidate from "vee-validate";
import zh_CN from "vee-validate/dist/locale/zh_CN"; //中文提示信息
Vue.use(VeeValidate);
// 表单验证和设置提示信息
VeeValidate.Validator.localize('zh_CN', {
messages: {
...zh_CN.messages,
is: (field) => `${field}必须与密码相同` // 修改内置规则的 message,让确认密码和密码相同
},
attributes: { // 给校验的 field 属性名映射中文名称
phone: '手机号',
code: '验证码',
password:'密码',
password1:'确认密码',
isCheck:'协议'
}
})
//自定义校验规则 (协议必须打勾同意)
VeeValidate.Validator.extend('agree', {
validate: value => {
return value
},
getMessage: field => field + '必须同意'
})
第三步:基本使用
{{ errors.first("phone") }}
点击按钮提交:
const success = await this.$validator.validateAll(); //保证全部表单验证成功后才发请求