CSS的特性:继承性、层叠性、优先级
优先级:写CSS样式的时候,会给同一个元素添加多个样式,此时谁的权重高就显示谁的样式
标签、类/伪类/属性、全局选择器、行内样式、id、!important
!important > 行内样式 > id > 类/伪类/属性 > 标签 > 全局选择器
display:none;
元素在页面上消失,不占据空间
opacity:0;
设置了元素的透明度为0,元素不可见,占据空间位置
visibility:hidden;
让元素消失,占据空间位置,一种不可见的状态
position:absolute;
clip-path
px是像素,显示器上给我们呈现画面的像素,每个像素的大小是一样,绝对单位长度
rem,相对单位,相对于html根节点的font-size的值,直接给html节点的font-size:62.5%;
1rem = 10px; (16px*62.5%=10px)
重排(回流):布局引擎会根据所有的样式计算出盒模型在页面上的位置和大小
重绘:计算好盒模型的位置、大小和其他一些属性之后,浏览器就会根据每个盒模型的特性进行绘制
浏览器的渲染机制
对DOM的大小、位置进行修改后,浏览器需要重新计算元素的这些几何属性,就叫重排
对DOM的样式进行修改,比如color和background-color,浏览器不需要重新计算几何属性的时候,直接绘制了该元素的新样式,那么这里就只触发了重绘
.father{
width: 400px;
height: 400px;
border: 1px solid;
position: relative;
}
.son{
position: absolute;
width: 200px;
height: 200px;
background-color: red;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.father{
width: 400px;
height: 400px;
border: 1px solid;
position: relative;
}
.son{
position: absolute;
width: 200px;
height: 200px;
background-color: blue;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.father{
display: flex;
justify-content: center;
align-items: center;
width: 400px;
height: 400px;
border: 1px solid;
}
.son{
width: 200px;
height: 200px;
background-color: green;
}
CSS的三大特性:继承、层叠、优先级
子元素可以继承父类元素的样式
预处理语言增加了变量、函数、混入等强大的功能
SASS LESS
<!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>Document</title>
<style>
/* .box{
}
span{
}
.box span{
}
p{
} */
/* .box{
} */
/* .box
font-size */
/* .box{
width: 200px;
span{
}
} */
@global: #eee;
.box{
color: @global;
}
</style>
</head>
<body>
<div class="box">
<span></span>
</div>
<p></p>
</body>
</html>
在写HTML页面结构时所用的标签有意义
头部用head 主体用main 底部用foot…
怎么判断页面是否语义化了?
把CSS去掉,如果能够清晰的看出来页面结构,显示内容较为正常
为什么要选择语义化?
1.让HTML结构更加清晰明了
2.方便团队协作,利于开发
3.有利于爬虫和SEO
4.能够让浏览器更好的去解析代码
5.给用户带来良好的体验
H5的新特性:
rem是相对长度,相对于根元素(html)的font-size属性来计算大小,通常来做移动端的适配
rem是根据根元素font-size计算值的倍数
比如html上的font-size:16px,给div设置宽为1.5rem,1.2rem = 16px*1.2 = 19.2px.
// a.js
(function flexible(window, document) {//首先是一个立即执行函数,执行的时候传入参数window,document
var docEl = document.documentElement //返回文档的root元素,即根元素html
var dpr = window.devicePixelRatio || 1 //获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
//调整body标签的fontSize
//设置默认字体的大小,默认字体的大小继承自body
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
//设置 1rem = viewWidth / 10
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit();
//在页面resize或者pageshow重新设置rem
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {//某些浏览器,重新展示页面时,走的是页面展示缓存
setRemUnit();
}
})
//检测0.5px的支持,支持则root元素的class有hairlines
//解决1px在高清屏多像素问题
if (dpr >= 2) {
var fakeBody = document.createElement('doby');
var testElement = document.createElement('div');
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
当设置样式overflow:scroll/auto时,IOS上的滑动会卡顿
-webkit-overflow-scrolling:touch;
在安卓环境下placeholder文字设置行高时会偏上
input有placeholder属性的时候不要设置行高
移动端字体小于12px时异常显示
应该先把在整体放大一倍,然后再用transform进行缩小
Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.
ios下input按钮设置了disabled属性为true显示异常
input[typy=button]{
opcity:1
}
安卓手机下取消语音输入按钮
input::-webkit-input-speech-button{
display:none
}
IOS下取消input输入框在输入引文首字母默认大写
禁用IOS和安卓用户选中文字
添加全局CSS样式:-webkit-user-select:none
禁止IOS弹出各种窗口
-webkit-touch-callout:none
禁止IOS识别长串数字为电话
添加meta属性
优点:
解决加载缓慢的第三方内容如图标和广告等的加载问题
Security sandbox
并行加载脚本
缺点:
iframe会阻塞主页面的Onload事件
即时内容为空,加载也需要时间
没有语意
首先:CSS 规范规定,每个元素都有 display 属性,确定该元素的类型,每个元素都有默认的 display 值,如 div 的 display 默认值为“block”,则为“块级”元素;span 默认 display 属性值为“inline”,是“行内”元素。
<div>、<p>、<h1>...<h6>、<ol>、<ul>、<dl>、<table>、<address>、<blockquote> 、<form>
<a>、<span>、<br>、<i>、<em>、<strong>、<label>、<q>、<var>、<cite>、<code>
<img>、<input>
<br/> <hr/> <img/> <input/> <link/> <meta/>
ECMAScript:JS的核心内容,描述了语言的基础语法,比如var,for,数据类型(数组、字符串),
文档对象模型(DOM):DOM把整个HTML页面规划为元素构成的文档
浏览器对象模型(BOM):对浏览器窗口进行访问和操作
String Boolean Number Array Object Function Math Date RegExp…
Math
abs() sqrt() max() min()
Data
new Data() getYear()
Array
String
concat() length slice() split()
1、sort( ):sort 排序 如果下面参数的正反 控制 升序和降序 ,返回的是从新排序的原数组
2、splice( ):向数组的指定index处插入 返回的是被删除掉的元素的集合,会改变原有数组;截取类 没有参数,返回空数组,原数组不变;一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的 数组,原数组改变;两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。会改变原数据
3、pop( ):从尾部删除一个元素 返回被删除掉的元素,改变原有数组。
4、push( ):向数组的末尾追加 返回值是添加数据后数组的新长度,改变原有数组。
5、unshift( ):向数组的开头添加 返回值是添加数据后数组的新长度,改变原有数组。
6、shift( ):从头部删除一个元素 返回被删除掉的元素,改变原有数组。
7、reverse( ): 原数组倒序 它的返回值是倒序之后的原数组
8、concat( ):数组合并。
9、slice( ):数组元素的截取,返回一个新数组,新数组是截取的元素,可以为负值。从数组中截取,如果不传参,会返回原数组。如果只传入一个参数,会从头部开始删除,直到数组结束,原数组不会改变;传入两个参数,第一个是开始截取的索引,第二个是结束截取的索引,不包含结束截取的这一项,原数组不会改变。最多可以接受两个参数。
10、join( ):讲数组进行分割成为字符串 这能分割一层在套一层就分隔不了了
11、toString( ):数组转字符串;
12、toLocaleString( ):将数组转换为本地数组。
13、forEach( ):数组进行遍历;
14、map( ):没有return时,对数组的遍历。有return时,返回一个新数组,该新数组的元素是经过过滤(逻辑处理)过的函数。
15、filter( ):对数组中的每一运行给定的函数,会返回满足该函数的项组成的数组。
16、every( ):当数组中每一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
17、some( ):当数组中有一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
18、reduce( ):回调函数中有4个参数。prev(之前计算过的值),next(之前计算过的下一个的值),index,arr。把数组列表计算成一个
19.isArray() 判断是否是数组
20. indexOf 找索如果找到了就会返回当前的一个下标,若果没找到就会反回-1
21. lastIndexOf 它是从最后一个值向前查找的 找索如果找到了就会返回当前的一个下标,若果没找到就会反回-1
22. Array.of() 填充单个值
23. Array.from() 来源是类数组
24.fill填充方法 可以传入3各参数 可以填充数组里的值也就是替换 如果一个值全部都替换掉 , 第一个参数就是值 第二个参数 从起始第几个 第三个参数就是最后一个
find 查找这一组数 符合条件的第一个数 给他返回出来
findIndex() 查找这一组数 符合条件的第一数的下标 给他返回出来 没有返回 -1
keys 属性名 values属性值 entries属性和属性值
forEach 循环遍历 有3个参数 无法使用 break continue , 参数一就是每个元素 参数二就是每个下标 参数三就是每个一项包扩下标和元素
### 改变数组本身的api
1. `pop()` 尾部弹出一个元素
2. `push()` 尾部插入一个元素
3. `shift()` 头部弹出一个元素
4. `unshift()` 头部插入一个元素
5. `sort([func])` 对数组进行排序,func有2各参数,其返回值小于0,那么参数1被排列到参数2之前,反之参数2排在参数1之前
6. `reverse()` 原位反转数组中的元素
7. `splice(pos,deleteCount,...item)` 返回修改后的数组,从pos开始删除deleteCount个元素,并在当前位置插入items
8. `copyWithin(pos[, start[, end]])` 复制从start到end(不包括end)的元素,到pos开始的索引,返回改变后的数组,浅拷贝
9. `arr.fill(value[, start[, end]])` 从start到end默认到数组最后一个位置,不包括end,填充val,返回填充后的数组
其他数组api不改变原数组
map 映射关系的数组 map 主要就是有返回值可以return 数组 判断的会返回boolean
1、map()方法返回一个新数组,新数组中的元素为原始数组中的每个元素调用函数处理后得到的值。
2、map()方法按照原始数组元素顺序依次处理元素。
注意:
map()不会对空数组进行检测。
map()不会改变原始数组。
map() 函数的作用是对数组中的每一个元素进行处理,返回新的元素。
filter 满足条件的都能返回 是一个数组
some返回boolean 循环数组 只要有一个成员通过了就会返回 true 反而 false
every返回boolean 循环数组 只有全部成员通过了就会返回 true 反而 false
reduce() 累加器 把上一次计算的值,给下一次计算进行相加
set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
delete [1] delete 可以删除数组中的一向
**Array.isArray()** 用于确定传递的值是否是一个 [`Array`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array)。
flat 扁平化 将嵌套的数组 “拉平”,变成一维数组。该方法返回一个新数组,对原数据没有影响。// 参数写的就是代表要扁平到第几层
//1、every()
var arr = [1,56,80,5];
var main = arr.every(n => n > 0);
console.log(main) //输出:true
//2、some()
var arr = [1,-56,80,-5];
var main = arr.some(n => n > 0);
console.log(main) //输出:true
//3、reducer()
var arr = [10,20,30,40]
let result = arr.reduce(function(prev,next,index,arr){
return prev + next;
})
console.log(result); //输出:100
// 4、filter 返回满足要求的数组项组成的新数组
var arr3 = [3,6,7,12,20,64,35]
var result3 = arr3.filter((item,index,arr)=>{
return item > 3
})
console.log(result3) //[6,7,12,20,64,35]
// 5、map 返回每次函数调用的结果组成的数组
var arr4 = [1,2]
var result4 = arr4.map((item,index,arr)=>{
return `${item}`
})
console.log(result4)
/*[ '1',
'2', ]*/
ES6数组的常用方法:
1、Array.from( ):将对象或字符串转成数组,注意得有length。
2、Array.of( ): 将一组值转换为数组。
3、copyWithin(target,start(可选),end(可选)):数组内数据的复制替换
target:从该位置开始替换数据;
start:从该位置开始读取数据,默认为0;
end:到该位置停止数据的读取,默认为数组的长度
4、find( ):用于找出第一个符合条件的数组成员。
5、findIndex( ):返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
6、fill(value,start,end):使用给定值,填充一个数组。
value:填充的值;
start:开始填充的位置;
end:填充结束的位置。
7、keys( ):对键名的遍历。
8、values( ):对键值的遍历。
9、entries( ):对键值对的遍历。
10、includes( ):数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。
11、flat( ):用于数组扁平,数组去除未定义。可以去除空项。
12、flatMap( ):对原数组的每个成员执行一个函数。
13、Map( ):是一组键值对的结构,具有极快的查找速度。
14、Set( ):Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
//1、Array.from() -- Array.of()
var arrayLink = {
"0":"a",
"1":"b",
"2":"c",
length:3
}
var arr = Array.from(arrayLink)
console.log(arr) // 输出: [a,b,c]
console.log(Array.from("abcdefg")) //输出:["a", "b", "c", "d", "e", "f", "g"]
console.log(Array.of(1,2,3,4,5)) //输出: [1, 2, 3, 4, 5]
//2、copyWithin()
var arr = [1,2,3,4,5];
var main = arr.copyWithin(0,3);
console.log(main); //输出:[4,5,3,4,5]
//3、find()
var arr = [1,-5,2,9,-6];
var main = arr.find(n => n < 0);
console.log(main); //输出:-5
//4、fill()
var arr = ["a","b","c","d"];
console.log(arr.fill(7,1,2));//输出:["a",7,"c","d"]
//5、keys() values() entries()
var arr = ["a","b","c","d"];
for(let index of arr.keys()){
console.log(index);
}
for(let elem of arr.values()){
console.log(elem);
}
for(let [index,elem] of arr.entries()){
console.log(index,elem);
}
//6、includes()
let arr = [12,34,223,45,67]
console.log(arr.includes(45)) //输出:true
[1, 2, NaN].includes(NaN) // true
[1, 2, NaN].indexOf(NaN) // -1
//7、Map
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
//初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
//由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
//8、Set
//要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
//重复元素在Set中自动被过滤:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"} 注意:数字3和字符串'3'是不同的元素
//通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:
s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // 仍然是 Set {1, 2, 3, 4}
//通过delete(key)方法可以删除元素:
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}
forEach():
// forEach
// item: 当前元素的值,调用函数必须要有的参数
// index: 当前元素的索引(下标),可选参数
// arrE: 当前元素所属的数组对象,可选参数
let arrE = [1,2,3,4,5]
arrE.forEach((item, index, arrE) => {
console.log(index, item, arrE)
})
注意:
item 进行修改的时候,如果 item 是原始类型的值,item 对应的 的内存地址实际并没有变化,
如果 item 是 引用类型的值,item 对应多的内存地址也没有变化,但是对应的值,已经修改
map():
//map
// item: 当前元素的值,调用函数必须要有的参数
// index: 当前元素的索引(下标),可选参数
// arrM: 当前元素所属的数组对象,可选参数
let arrM = [4, 9, 16, 25]
let arrMq = arrM.map((item, index, arrM) => {
console.log(index, item, arrM)
return Math.sqrt(item)
})
console.log('------------')
console.log(arrM)
console.log(arrMq)
filter():
//filter
// item: 当前元素的值,调用函数必须要有的参数
// index: 当前元素的索引(下标),可选参数
// arrF: 当前元素所属的数组对象,可选参数
let arrF = [1, 2, 3, 4, 5, 4, 2, 3, 6]
// 求偶数
let arrFc = arrF.filter((item, index, arrF) => {
console.log(index, item, arrF)
return item % 2 == 0
})
console.log('----------')
console.log('偶数集合:',arrFc)
console.log('原数组:', arrF)
//数组去重
let arrFs = arrF.filter((item, index) => {
//当前元素 在原数组中的第一个索引 === 当前索引值,否则返回当前元素
return arrF.indexOf(item, 0) === index;
})
console.log('----------')
console.log('数组去重: ', arrFs)
console.log('原数组:', arrF)
哪些方法会改变原数组?
push() pop() unshift() shift() sort() reverse() splice()
typeof()
instanceof()
constructor
Object.prototype.toString.call()
// typeof () 对于基本数据类型没问题,遇到引用数据类型就不管用
console.log( typeof 666 ) //number
console.log( typeof [1,2,3] ) //object
// instanceof () 只能判断引用数据类型,不能判断基本数据类型
console.log( [] instanceof Array ) //true
console.log( 'abc' instanceof String ) //false
// constructor 几乎可以判断基本数据类型和引用数据类型;如果声明了一个构造函数,并把它的原型指向了Array
console.log( ('abc').constructor === String ) //object
// Object.prototype.toString.call()
var opt = Object.prototype.toString
console.log( opt.call(2) )
console.log( opt.call(true) )
console.log( opt.call('aaa') )
console.log( opt.call([]) )
console.log( opt.call({}) )
什么是闭包?函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存再内存中,不会被垃圾回收机制回收
缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄漏
使用场景:防抖,节流,函数嵌套函数避免全局污染的时候
function fn(a){
return function(){
console.log(a)
}
}
var fo = fn('abcd')
fo()
JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。
垃圾回收机制
因素:一些为生命直接赋值的变量;一些未清空的定时器;过度的闭包;一些引用元素没有被清除。
又叫事件代理,原理就是利用了事件冒泡的机制来实现,也就是说把子元素的事件绑定到了父元素的身上
如果子元素阻止了事件冒泡,那么委托也就不成立
阻止事件冒泡:event.stopPropagation()
addEventListener(‘click’,函数名,true/false) 默认是false(事件冒泡),true(事件捕获)
好处:提高性能,减少事件的绑定,也就减少了内存的占用。
基本数据类型:String Number Boolean undefined null
基本数据类型保存在栈内存当中,保存的就是一个具体的值
引用数据类型(复杂数据类型):Object Function Array
保存在堆内存当中,声明一个引用类型的变量,它保存的是引用类型数据的地址
假如声明两个引用类型同时指向了一个地址的时候,修改其中一个那么另外一个也会改变
var obj = {
name:'张三',
age:18
}
var obj1 = obj
obj1.name = '李四'
console.log(obj)
console.log(obj1)
原型就是一个普通对象,它是为构造函数的实例共享属性和方法;所有实例中引用的原型都是同一个对象
使用prototype可以把方法挂在原型上,内存值保存一份
__proto__可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)
function Person(){
}
Person.prototype.look = function(){
console.log('西游记')
}
var p1 = new Person()
var p2 = new Person()
// p1.say()
// p2.say()
p1.look()
p2.look()
console.log( p1.__proto__ === Person.prototype )
function newFun(Fun,...args){
// 1.先创建一个空对象
let newObj = {}
// 2.把空对象和构造函数通过原型链进行链接
newObj.__proto__ = Fun.prototype
// 3.把构造函数的this绑定到新的空对象身上
const result = Fun.apply(newObj,args)
// 4.根据构建函数返回的类型判断,如果是值类型,则返回对象,如果是引用类型,就要返回这个引用类型
return result instanceof Object ? result : newObj
}
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log('123456')
}
const p1 = newFun(Person,'张三')
p1.say()
console.log(p1)
// 1.原型链继承
// 让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性。
// function Parent() {
// this.isShow = true
// this.info = {
// name: "abc",
// age: 18,
// };
// }
// Parent.prototype.getInfo = function () {
// console.log(this.info);
// console.log(this.isShow);
// }
// function Child() { };
// Child.prototype = new Parent();
// let Child1 = new Child();
// Child1.info.gender = "男";
// Child1.getInfo(); // {name: 'abc', age: 18, gender: '男'} ture
// let child2 = new Child();
// child2.isShow = false
// console.log(child2.info.gender) // 男
// child2.getInfo(); // {name: 'abc', age: 18, gender: '男'} false
// 优点:写法方便简洁,容易理解。
// 缺点:对象实例共享所有继承的属性和方法。无法向父类构造函数传参。
// 2.借用构造函数继承
// 在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
// function Parent(gender) {
// this.info = {
// name: "yyy",
// age: 18,
// gender: gender
// }
// }
// function Child(gender) {
// Parent.call(this, gender)
// }
// let child1 = new Child('男');
// child1.info.nickname = 'xxxx'
// console.log(child1.info);
// let child2 = new Child('女');
// console.log(child2.info);
// 优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
// 缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。
// 在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
// 3.组合继承
// 将 原型链 和 借用构造函数 的组合到一块。使用原型链实现对原型属性和方法的继承,
// 而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,
// 又能够保证每个实例都有自己的属性
// function Person(gender) {
// console.log('执行次数');
// this.info = {
// name: "yyy",
// age: 19,
// gender: gender
// }
// }
// Person.prototype.getInfo = function () { // 使用原型链继承原型上的属性和方法
// console.log(this.info.name, this.info.age)
// }
// function Child(gender) {
// Person.call(this, gender) // 使用构造函数法传递参数
// }
// Child.prototype = new Person()
// let child1 = new Child('男');
// child1.info.nickname = 'xxx'
// child1.getInfo()
// console.log(child1.info);
// let child2 = new Child('女');
// console.log(child2.info);
// 优点:就是解决了原型链继承和借用构造函数继承造成的影响。
// 缺点:是无论在什么情况下,都会调用两次父类构造函数:
// 一次是在创建子类原型的时候,另一次是在子类构造函数内部
// 4.ES6、Class实现继承
// Class通过extends关键字实现继承,其实质是先创造出父类的this对象,
// 然后用子类的构造函数修改this
// 子类的构造方法中必须调用super方法,且只有在调用了super()之后才能使用this,
// 因为子类的this对象是继承父类的this对象,然后对其进行加工,
// 而super方法表示的是父类的构造函数,用来新建父类的this对象
class Animal {
constructor(kind) {
this.kind = kind
}
getKind() {
return this.kind
}
}
// 继承Animal
class Cat extends Animal {
constructor(name) {
// 子类的构造方法中必须先调用super方法
super('cat');
this.name = name;
}
getCatInfo() {
console.log(this.name + ':' + super.getKind())
}
}
const cat1 = new Cat('buding');
cat1.getCatInfo(); // buding:cat
// 优点:语法简单易懂,操作更方便。
// 缺点:并不是所有的浏览器都支持class关键字
JS引擎 运行上下文 调用栈 事件循环 回调
当没有async和defer这两个属性的时候,
浏览器会立刻加载并执行指定的脚本
有async
加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
有defer
加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等
所有元素解析完成之后才会执行
HTML5规定的内容:
setTimeout最小执行时间是4ms
setInterval最小执行时间是10ms
JS的组成:ECMAScript BOM DOM
ES5:ECMAScript5,2009年ECMAScript的第五次修订,ECMAScript2009
ES6:ECMAScript6,2015年ECMAScript的第六次修订,ECMAScript2015,是JS的下一个版本标准
都是改变this指向和函数的调用,call和apply的功能类似,只是传参的方法不同
call方法传的是一个参数列表
apply传递的是一个数组
bind传参后不会立刻执行,会返回一个改变了this指向的函数,这个函数还是可以传参的,bind()()
call方法的性能要比apply好一些,所以call用的更多一点
如果一个函数内可以调用函数本身,那么这个就是递归函数
函数内部调用自己
特别注意:写递归必须要有退出条件return
深拷贝就是完全拷贝一份新的对象,会在堆内存中开辟新的空间,拷贝的对象被修改后,原对象不受影响
主要针对的是引用数据类型
// 1.扩展运算符
// let obj = {
// name:'张三',
// age:18
// }
// let obj1 = {...obj}
// obj1.name = '王五'
// console.log(obj)
// console.log(obj1)
// 缺点:这个方法只能实现第一层,当有多层的时候还是浅拷贝
// 2.JSON.parse(JSON.stringify())
// let obj = {
// name:'张三',
// age:18,
// say(){
// console.log('say hello')
// }
// }
// let obj1 = JSON.parse(JSON.stringify(obj))
// obj1.name = '王五'
// console.log(obj)
// console.log(obj1)
// 缺点:该方法并不会拷贝内部函数
// 3.利用递归函数实现
let origin = {
name:'张三',
age:18,
say(){
console.log('say hello')
},
arr:[[1,2],3,4,5]
}
function exten(origin,deep){
let obj = {}
if( origin instanceof Array ){
obj = []
}
for( let key in origin ){
let value = origin[key]
obj[key] = (!!deep && typeof value === 'object' && value !== null) ? exten(value,deep) :value
}
return obj
}
const oo = exten(origin,true)
oo.arr[0].push(888)
console.log(origin)
console.log(oo)
手写深拷贝
function deepClone(obj){
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === 'object') {
for(let key in obj){
if (obj[key] && typeof obj[key] === 'object'){ // 判断对象的这条属性是否为对象
objClone[key] = deepClone(obj[key]); // 若是对象进行嵌套调用
}else{
objClone[key] = obj[key]
}
}
}
return objClone; //返回深度克隆后的对象
}
let arrayA = [{a: 1, b: 2, aa: [{ab: 1, ac: [{ac: 1}]}]}];
let arrayB = deepClone(arrayA);
arrayB[0]['aa'][0].ab = 2;
console.log(arrayA); // [{a: 1, b: 2, aa: [{ab: 1, ac: [{ac: 1}]}]}]
console.log(arrayB); // [{a: 1, b: 2, aa: [{ab: 2, ac: [{ac: 1}]}]}]
浅拷贝:只是增加了一个指针,指向已经存在的内存地址。
仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变。
浅拷贝的实现方式:
Object.assign()是es6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target,source_1,…),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。
同样Object.assign可以做对象的合并
用法:将第2个参数开始的对象合并到第一个对象中
let newObj = Object.assign(...obj1,...obj2)//---如果是这样写,会改变原对象的数据
// 为了不破坏原对象,所以写入一个空对象{}
let newObj = Object.assign({},...obj1,...obj2)
let target = {a:1};
let object2 = {b:2};
let object3 = {c:3};
Object.assign(target,object2,object3);
console.log(target);// {a:1,b:2,c:3}
使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。
语法:let cloneObj = {…obj};
let obj1 = {a:1,b:{c:1}};
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1);// {a:2,b:{c:1}}
console.log(obj2);// {a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1);// {a:2,b:{c:2}}
console.log(obj2);// {a:1,b:{c:2}}
a.Array.prototype.slice()
slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:
用法:array.slice(start,end),该方法不会改变原始数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4]
console.log(arr);// [1,2,3,4]
console.log(arr.slice());// [1,2,3,4]
console.log(arr.slice()==arr);// false
console.log(arr.slice()===arr);// false
start:①提取起始处的索引(从0开始)
②如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)
eg:arr.slice(-2)//[3,4]
③如果省略start,则slice从索引0开始。
④如果start超出原数组的索引范围,则会返回空数组。
end:①提取终止处的索引(从0开始)。
slice会提取原数组中索引从start到end的所有元素(包括start,但不包括end)
②如果该参数为负数,则它表示在原数组的倒数第几个元素结束抽取。
eg:slice(-2,-1)//[3]→(抽取了原数组中的倒数第二个元素到最后一个元素但不包含最后一个元素)
③如果end被省略,则slice会一直提取到原数组末尾。
④如果end大于数组的长度,slice也会一直提取到原数组末尾。
b.Array.prototype.concat()
concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr);// [1,2,3,4]
console.log(arr.concat());// [1,2,3,4]
console.log(arr.concat()==arr);// false
console.log(arr.concat()===arr);// false
function shallowCopy(object) {
// 只拷贝对象
if(!object || typeof object !== 'object') return;
// 根据object的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object)?[]:{};
// 遍历object,并且判断是object的属性才拷贝
for(let key in object) {
if(object.hasOwnProperty(key)){
newObject[key] = object[key];
}
}
return newObject;
}
JS是一个单线程的脚本语言
主线程 执行栈 任务队列 宏任务 微任务
主线程先执行同步任务,然后才去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么要先执行微任务
全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环!
创建交互式网页应用的网页开发技术
在不重新加载整个网页的前提下,与服务器交换数据并更新部分内容
通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,最后通过JS操作DOM更新页面
Promise对象,封装了一个异步操作并且还可以获取成功或失败的结果
Promise主要就是解决回调地狱的问题,之前如果异步任务比较多,同时他们之间有相互依赖的关系,
就只能使用回调函数处理,这样就容易形成回调地狱,代码的可读性差,可维护性也很差
有三种状态:pending初始状态 fulfilled成功状态 rejected失败状态
状态改变只会有两种情况,
pending -> fulfilled; pending -> rejected 一旦发生,状态就会凝固,不会再变
首先就是我们无法取消promise,一旦创建它就会立即执行,不能中途取消
如果不设置回调,promise内部抛出的测u哦呜就无法反馈到外面
若当前处于pending状态时,无法得知目前在哪个阶段。
原理:
构造一个Promise实例,实例需要传递函数的参数,这个函数有两个形参,分别都是函数类型,一个是resolve一个是reject
promise上还有then方法,这个方法就是来指定状态改变时的确定操作,resolve是执行第一个函数,reject是执行第二个函数
token:验证身份的令牌,一般就是用户通过账号密码登录后,服务端把这些凭证通过加密等一系列操作后得到的字符串
DNS解析
建立TCP连接
发送HTTP请求
服务器处理请求
渲染页面
浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树
再把CSS解析成CSSOM
把DOM和CSSOM合并为渲染树
布局
把渲染树的每个节点渲染到屏幕上(绘制)
断开TCP连接
DOM树是和HTML标签一一对应的,包括head和隐藏元素
渲染树是不包含head和隐藏元素
精灵图:把多张小图整合到一张大图上,利用定位的一些属性把小图显示在页面上,当访问页面可以减少请求,提高加载速度
base64:传输8Bit字节代码的编码方式,把原本二进制形式转为64个字符的单位,最后组成字符串
base64是会和html css一起下载到浏览器中,减少请求,减少跨域问题,但是一些低版本不支持,若base64体积比原图片大,不利于css的加载。
基于XML语法格式的图像格式,可缩放矢量图,其他图像是基于像素的,SVG是属于对图像形状的描述,本质是文本文件,体积小,并且不管放大多少倍都不会失真
<svg></svg>
<img src="pic.svg" />
JSON Web Token 通过JSON形式作为在web应用中的令牌,可以在各方之间安全的把信息作为JSON对象传输
信息传输、授权
JWT的认证流程
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题。
使用场景:
a. 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
b. 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
c. 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
node package manager,node的包管理和分发工具,已经成为分发node模块的标准,是JS的运行环境
npm的组成:网站、注册表、命令行工具
let a = [1,2]
Object.prototype.toString.call(a) // '[object Array]'
强缓存(本地缓存)、协商缓存(弱缓存)
强缓:不发起请求,直接使用缓存里的内容,浏览器把JS,CSS,image等存到内存中,下次用户访问直接从内存中取,提高性能
协缓:需要像后台发请求,通过判断来决定是否使用协商缓存,如果请求内容没有变化,则返回304,浏览器就用缓存里的内容
强缓存的触发:
HTTP1.0:时间戳响应标头
HTTP1.1:Cache-Control响应标头
协商缓存触发:
HTTP1.0:请求头:if-modified-since 响应头:last-modified
HTTP1.1:请求头:if-none-match 响应头:Etag
http:// www.ccc.com:8080/index/vue.js
协议 子域名 主域名 端口号 资源
同源策略是浏览器的核心,如果没有这个策略就会遭受网络攻击
主要指的就是协议+域名+端口号三者一致,若其中一个不一样则不是同源,会产生跨域
三个允许跨域加载资源的标签:img link script
跨域是可以发送请求,后端也会正常返回结果,只不过这个结果被浏览器拦截了!
JSONP
CORS
websocket
反向代理
都是应对页面中频繁触发事件的优化方案。
区别:防抖只会在最后一次事件后执行触发函数,节流不管事件多么的频繁,都会保证在规定时间段内触发事件函数。
原理是维护一个定时器,将很多个相同的操作合并成一个。规定在delay后触发函数,如果在此之前触发函数,则取消之前的计时重新计时,只有最后一次操作能被触发。例如:实时搜索的input,一直输入就不发送。
let input = document.querySelector("input");
let time = null;//time用来控制事件的触发
input.addEventListener('input',function(){
//防抖语句,把以前的定时删除,只执行最后一次
if(time !== null){
clearTimeout(time);
}
time = setTimeout(() => {
console.log(this.value);//业务实现语句,这里的this指向的是input
},500)
})
//节流
function throttle(fn, time) {//连续触发事件 规定的时间
let flag = false;
return function () {
//使用标识判断是否在规定的时间内重复触发了函数,没有就触发,有就不触发
if (!flag) {//不为假时 执行以下
fn();//触发事件
flag = true;//为真
setTimeout(() => {//超时调用(在规定的时间内只执行一次)
flag = false;
}, time);
}
}
}
mybtn.onclick = throttle(btn, 3000);//单击事件 节流(btn,3s时间)
JSON是一种纯字符串形式的数据,它本身不提供任何方法,适合在网络中进行传输
JSON数据存储在.json文件中,也可以把JSON数据以字符串的形式保存在数据库、Cookise中
JS提供了JSON.parse() JSON.stringify()
什么时候使用json:定义接口;序列化;生成token;配置文件package.json
json优点:json数据更小、读取速度更快、更容易被解析(因为与js格式类似,可读性更好)
可以在渲染数据的地方给一些默认的值
if判断语句
分片上传:
<!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>Document</title>
</head>
<body>
</body>
<script>
// 读取文件内容:
const input = document.querySelector('input');
input.addEventListener('change', function () {
var file = this.files[0];
});
// 可以使用md5实现文件的唯一性
const md5code = md5(file);
// 然后开始对文件进行分割
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener("load", function (e) {
//每10M切割一段,这里只做一个切割演示,实际切割需要循环切割,
var slice = e.target.result.slice(0, 10 * 1024 * 1024);
});
// 上传一个(一片)
const formdata = new FormData();
formdata.append('0', slice);
// formdata.append('filename', file.filename);
formdata.append('filename', md5code+'.'+fileType);
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
//xhr.responseText
});
xhr.open('POST', '');
xhr.send(formdata);
xhr.addEventListener('progress', updateProgress);
xhr.upload.addEventListener('progress', updateProgress);
function updateProgress(event) {
if (event.lengthComputable) {
//进度条
}
}
// 图片和视频的文件类型判断
function checkFileType(type, file, back) {
/**
* type png jpg mp4 ...
* file input.change=> this.files[0]
* back callback(boolean)
*/
var args = arguments;
if (args.length != 3) {
back(0);
}
var type = args[0]; // type = '(png|jpg)' , 'png'
var file = args[1];
var back = typeof args[2] == 'function' ? args[2] : function () { };
if (file.type == '') {
// 如果系统无法获取文件类型,则读取二进制流,对二进制进行解析文件类型
var imgType = [
'ff d8 ff', //jpg
'89 50 4e', //png
'0 0 0 14 66 74 79 70 69 73 6F 6D', //mp4
'0 0 0 18 66 74 79 70 33 67 70 35', //mp4
'0 0 0 0 66 74 79 70 33 67 70 35', //mp4
'0 0 0 0 66 74 79 70 4D 53 4E 56', //mp4
'0 0 0 0 66 74 79 70 69 73 6F 6D', //mp4
'0 0 0 18 66 74 79 70 6D 70 34 32', //m4v
'0 0 0 0 66 74 79 70 6D 70 34 32', //m4v
'0 0 0 14 66 74 79 70 71 74 20 20', //mov
'0 0 0 0 66 74 79 70 71 74 20 20', //mov
'0 0 0 0 6D 6F 6F 76', //mov
'4F 67 67 53 0 02', //ogg
'1A 45 DF A3', //ogg
'52 49 46 46 x x x x 41 56 49 20', //avi (RIFF fileSize fileType LIST)(52 49 46 46,DC 6C 57 09,41 56 49 20,4C 49 53 54)
];
var typeName = ['jpg', 'png', 'mp4', 'mp4', 'mp4', 'mp4', 'mp4', 'm4v', 'm4v', 'mov', 'mov', 'mov', 'ogg', 'ogg', 'avi'];
var sliceSize = /png|jpg|jpeg/.test(type) ? 3 : 12;
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener("load", function (e) {
var slice = e.target.result.slice(0, sliceSize);
reader = null;
if (slice && slice.byteLength == sliceSize) {
var view = new Uint8Array(slice);
var arr = [];
view.forEach(function (v) {
arr.push(v.toString(16));
});
view = null;
var idx = arr.join(' ').indexOf(imgType);
if (idx > -1) {
back(typeName[idx]);
} else {
arr = arr.map(function (v) {
if (i > 3 && i < 8) {
return 'x';
}
return v;
});
var idx = arr.join(' ').indexOf(imgType);
if (idx > -1) {
back(typeName[idx]);
} else {
back(false);
}
}
} else {
back(false);
}
});
} else {
var type = file.name.match(/\.(\w+)$/)[1];
back(type);
}
}
// 调用方法
checkFileType('(mov|mp4|avi)', file, function (fileType) {
// fileType = mp4,
// 如果file的类型不在枚举之列,则返回false
});
</script>
</html>
断点续传:
服务端返回,从哪里开始 浏览器自己处理
**跨域的原理:**指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript 实施的安全限制,那么只要协议、域名、端口有任何一个不同,都被当作是不同的域。跨域原理,即是通过各种方式,避开浏览器的安全限制。
JSONP: 通 过 动 态 创 建 script , 再 请 求 一 个 带 参 网 址 实 现 跨 域 通 信 。
**document.domain + iframe 跨域:**两个页面都通过 js 强制设置 document.domain为基础主域,就实现了同域。
**location.hash + iframe 跨域:**a 欲与 b 跨域相互通信,通过中间页 c 来实现。三个页面,不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js访问来通信。
**window.name + iframe 跨域:**通过 iframe 的 src 属性由外域转向本地域,跨域数据即由 iframe 的window.name 从外域传递到本地域。
**postMessage 跨域:**可以跨域操作的 window 属性之一。
**CORS:**服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,若要带cookie 请求,前后端都需要设置。
**代理跨域:**启一个代理服务器,实现数据的转发
jsonp是一个能够跨域的方法,由于ajax受到同源策略的影响,不能进行跨域,而script标签中的src属性中的链接能够访问跨域的js脚本,利用这个特性返回一段调用某个函数的js代码,在src中进行了调用,从而实现跨域
优点:可以跨越同源策略、可以在老版本浏览器中运行、请求完毕后可以通过callback的方式回传结果
缺点:只支持get请求、只支持跨域http请求、安全性差
- 运行效率
- 对基础的支持不够
webpack会把js css image看作一个模块,用import/require引入
找到入口文件,通过入口文件找到关联的依赖文件,把他们打包到一起
把bundle文件,拆分成多个小的文件,异步按需加载所需要的文件
如果一个被多个文件引用,打包时只会生成一个文件
如果引用的文件没有调用,不会打包,如果引入的变量和方法没有调用也不会打包
对于多个入口文件,加入引入了相同的代码,可以用插件把他抽离到公共文件中
都可以控制元素的显示和隐藏
是Model-View-ViewModel的缩写。前端开发的架构模式
M:模型,对应的就是data的数据
V:视图,用户界面,DOM
VM:视图模型:Vue的实例对象,连接View和Model的桥梁
核心是提供对View和ViewModel的双向数据绑定,当数据改变的时候,ViewModel能监听到数据的变化,自动更新视图,当用户操作视图的时候,ViewModel也可以监听到视图的变化,然后通知数据进行改动,这就实现了双向数据绑定
ViewModel通过双向绑定把View和Model连接起来,他们之间的同步是自动的,不需要认为干涉,所以我们只需要关注业务逻辑即可,不需要操作DOM,同时也不需要关注数据的状态问题,因为她是由MVVM统一管理
key属性是DOM元素的唯一标识
作用:
组件从创建到销毁的过程就是它的生命周期
创建
beforeCreat
在这个阶段属性和方法都不能使用
created
这里时实例创建完成之后,在这里完成了数据监测,可以使用数据,修改数据,不会触发updated,也不会更新视图
挂载
beforeMount
完成了模板的编译,虚拟DOM也完成创建,即将渲染,修改数据,不会触发updated
Mounted
把编译好的模板挂载到页面,这里可以发送异步请求也可以访问DOM节点
更新
beforeUpdate
组件数据更新之前使用,数据是新的,页面上的数据时旧的,组件即将更新,准备渲染,可以改数据
updated
render重新做了渲染,这时数据和页面都是新的,避免在此更新数据
销毁
beforeDestroy
实例销毁前,在这里实例还可以用,可以清楚定时器等等
destroyed
组件已经被销毁了,全部都销毁
使用了keep-alive时多出两个周期:
activited
组件激活时
deactivited
组件被销毁时
created:在渲染前调用,通常先初始化属性,然后做渲染
mounted:在模板渲染完成后,一般都是初始化页面后,在对元素节点进行操作
在这里请求数据可能会出现闪屏的问题,created里不会
一般用created比较多
请求的数据对DOM有影响,那么使用created
如果请求的数据对DOM无关,可以放在mounted
1.事件修饰符
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 内部元素触发的事件先在次处理
.self 只有在event.target是当前元素时触发
.once 事件只会触发一次
.passive 立即触发默认行为
.native 把当前元素作为原生标签看待
2.按键修饰符
.keyup 键盘抬起
.keydown 键盘按下
3.系统修饰符
.ctrl
.alt
.meta
4.鼠标修饰符
.left 鼠标左键
.right 鼠标右键
.middle 鼠标中键
5.表单修饰符
.lazy 等输入完之后再显示
.trim 删除内容前后的空格
.number 输入是数字或转为数字
<!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>Document</title>
</head>
<body>
</body>
<script>
<el-form : model="ruleForm" : rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="项目编码" prop="code">
<el-input v-model="ruleForm.code"></el-input>
</el-form-item>
</el-form>
//data中写rules规则,是一个数组,名字是标签里面的prop属性值
rules: {
code: [
{ required: true, message: '请输入项目编码', trigger: 'blur' },
]
}
<el-form-item label="邮箱号" rules="[{required:true,message:'请输入邮箱号',trigger:'blur'},{validator: yanz, trigger: 'blur'}]" : label-width="formLabelWidth" prop="email">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
data() {
// 自定义校验规则
var bargainMoney = (rule, value, callback) => {
// rule 对应使用bargainMoney自定义验证的 对象
// value 对应使用bargainMoney自定义验证的 数值
// callback 回调函数
const r = /^+?[1-9][0-9]*$/; // 正整数
if (value == null || String(value).trim() === "") {
callback(new Error("不能为空"));
} else if (!r.test(value)) {
callback(new Error("请输入正整数"));
} else {
callback();
}
};
return {
formData: {
haggleNumber: "", // 砍价人数
},
rules: {
haggleNumber: [
{
required: true,
validator: bargainMoney,
trigger: "blur"
}
],
}
}
}
// ---------------------------------------------
data() {
return {
formData: {
haggleNumber: "", // 砍价人数
},
rules: {
haggleNumber: [
{
required: true,
validator: this.bargainMoney,
trigger: "blur"
}
]
}
}
},
methods: {
// 自定义校验规则
bargainMoney(rule, value, callback){
// rule 对应使用bargainMoney自定义验证的 对象
// value 对应使用bargainMoney自定义验证的 数值
// callback 回调函数
const r = /^+?[1-9][0-9]*$/; // 正整数
if (value == null || String(value).trim() === "") {
return callback(new Error("不能为空"));
} else if (!r.test(value)) {
return callback(new Error("请输入正整数"));
} else {
return callback();
}
}
}
</script>
</html>
// fu组件
<template>
<div class="so">
<TodoItem :msg="a + b"></TodoItem>
<TodoItem v-bind:msg="msg"></TodoItem>
</div>
</template>
<script>
import TodoItem from "so";
export default {
components: {
TodoItem
},
data() {
return {
a: '我是组件1',
b: '我是组件2',
msg: '我是组件' + Math.random()
}
}
}
</script>
<style scoped>
.so {
margin: 20px auto 20px 20px;
}
</style>
// 子组件
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
name: "so",
props: ['msg']
}
</script>
r e f 引用信息会注册在父组件的 ref 引用信息会注册在父组件的 ref引用信息会注册在父组件的refs对象上
// fu组件
<template>
<div class="item">
<TodoItem ref="msg"></TodoItem>
<p ref="dom"></p>
</div>
</template>
<script>
import TodoItem from "./components/TodoItem.vue";
export default {
components: {
TodoItem
},
mounted() {
console.log(this.$refs)
this.$refs.msg.getmsg("我是fw");
this.$refs.dom.innerHTML = "我是普通元素哦";
}
}
</script>
<style scoped>
.item {
margin: 20px auto 20px 20px;
}
</style>
// 子组件
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
data() {
return {
msg: ''
}
},
methods: {
getmsg(s) {
this.msg = s;
}
}
}
</script>
// 父
<template>
<div class="item">
<TodoItem @getMsg="showMsg"></TodoItem>
{{ title }}
</div>
</template>
<script>
import TodoItem from "so.vue";
export default {
components: {
TodoItem
},
data() {
return {
title: ''
}
},
methods: {
showMsg(title) {
this.title = title;
}
}
}
</script>
<style scoped>
.item {
margin: 20px auto 20px 20px;
}
</style>
// 子
<template>
<div>
<p>子组件</p>
</div>
</template>
<script>
export default {
mounted() {
this.$emit('getMsg', '我从子组件过来的')
}
}
</script>
//定义中央事件总线
const EventBus = new Vue();
// 将中央事件总线赋值到 Vue.prototype 上,这样所有组件都能访问到了
Vue.prototype.$EventBus = EventBus;
const app = new Vue({
el: '#app',
template: `
`
});
// 组件 A
Vue.component('A', {
template: `
this is A component!
`,
data() {
return {
mymessage: 'hello brother1'
}
},
methods: {
passData(val) {
//触发全局事件globalEvent
this.$EventBus.$emit('globalEvent', val)
}
}
});
// 组件 B
Vue.component('B', {
template:`
this is B component!
组件A 传递过来的数据:{{brothermessage}}
`,
data() {
return {
mymessage: 'hello brother2',
brothermessage: ''
}
},
mounted() {
//绑定全局事件globalEvent
this.$EventBus.$on('globalEvent', (val) => {
this.brothermessage = val;
});
}
});
Vue的一个内置组件,包裹组件的时候,会缓存不活跃的组件实例,并不是销毁他们
作用:把组件切换的状态保存在内存里,防止重复渲染DOM节点,减少加载时间和性能消耗,提高用户体验
// 1.缓存所有页面
在 App.vue 里面
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
// 2.根据条件缓存页面
在 App.vue 里面
<template>
<div id="app">
// 1. 将缓存 name 为 test 的组件
<keep-alive include='test'>
<router-view/>
</keep-alive>
// 2. 将缓存 name 为 a 或者 b 的组件,结合动态组件使用
<keep-alive include='a,b'>
<router-view/>
</keep-alive>
// 3. 使用正则表达式,需使用 v-bind
<keep-alive :include='/a|b/'>
<router-view/>
</keep-alive>
// 5.动态判断
<keep-alive :include='includedComponents'>
<router-view/>
</keep-alive>
// 5. 将不缓存 name 为 test 的组件
<keep-alive exclude='test'>
<router-view/>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
3.结合Router,缓存部分页面
在 router 目录下的 index.js 文件里import Vue from 'vue'
import Router from 'vue-router'
const Home = resolve => require(['@/components/home/home'], resolve)
const Goods = resolve => require(['@/components/home/goods'], resolve)
const Ratings = resolve => require(['@/components/home/ratings'], resolve)
const Seller = resolve => require(['@/components/home/seller'], resolve)
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home,
redirect: 'goods',
children: [
{
path: 'goods',
name: 'goods',
component: Goods,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: 'ratings',
name: 'ratings',
component: Ratings,
meta: {
keepAlive: true // 需要缓存
}
},
{
path: 'seller',
name: 'seller',
component: Seller,
meta: {
keepAlive: true // 需要缓存
}
}
]
}
]
})
在 App.vue 里面
<template>
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
下载 创建实例 接着封装请求响应拦截器 抛出 最后封装接口
// axios.js
//引入axios
import axios from 'axios'
//创建实例
const api = axios.create({
//请求地址的公共部分
baseURL:'',
//请求的超时时间
timeout:3000
})
//axios拦截器
api.interceptors.request.use(config => {
//config 请求的信息
return config
},err => {
//抛出错误
Promise.reject(err)
})
api.interceptors.response.use(res => {
console.log(res)
return Promise.resolve(res)
},err=>{
//抛出错误
Promise.reject(err)
})
export default api
// request.js
import api from 'axios.js'
export const login = () => api({
url:'',
method:'get',
params:params
})
//使用
import { login } from 'request.js'
method:{
login().then(res => {
console.log(res)
})
}
params传参
this.$router.push({name:'index',params:{id:item.id}})
this.$route.params.id
路由属性传参
this.$router.push({name:'/index/${item.id}'})
路由配置 { path:'/index:id' }
query传参(可以解决页面刷新参数丢失的问题)
this.$router.push({
name:'index',
query:{id:item.id}
})
路由拦截 axios拦截
需要在路由配置中添加一个字段,它是用于判断路由是否需要拦截
{
name:'index',
path:'/index',
component:Index,
meta:{
requirtAuth:true
}
}
router.beforeEach((to,from,next) => {
if(to.meta.requirtAuth){
if( store.satte.token ){
next()
}else{ }
}
})
要在路由配置里设置meat属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转
根据用户登录的账号,返回用户角色
前端再根据角色,跟路由表的meta.role进行匹配
把匹配搭配的路由形成可访问的路由
// 1. window.location.reload()
// 2. matcher
const router = createRouter()
export function resetRouter(){
const newRouter = creatRouter()
router.matcher = newRouter.matcher
}
vuex肯定会重新获取数据,页面也会丢失数据
1.把数据直接保存在浏览器缓存里(cookie localstorage sessionstorage)
2.页面刷新的时候,再次请求数据,达到可以动态更新的方法
监听浏览器的刷新书简,在刷新前把数据保存到sessionstorage里,刷新后请求数据,请求到了用vuex,如果没有那就用sessionstorage里的数据
state 存储变量
getters state的计算属性
mutations 提交更新数据的方法
actions 和mutations差不多,他是提交mutations来修改数据,可以包括异步操作
modules 模块化vuex
使用场景:
用户的个人信息、购物车模块、订单模块
通过数据劫持和发布订阅者模式来实现,同时利用Object.defineProperty()劫持各个属性的setter和getter,
在数据发生改变的时候发布消息给订阅者,触发对应的监听回调渲染视图,也就是说数据和视图时同步的,数据发生改变,视图跟着发生改变,视图改变,数据也会发生改变。
第一步:需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter
第二步:compile模板解析指令,把模板中的变量替换成数据,然后初始化渲染视图,同时把每个指令对应的节点绑定上更新函数,添加订阅者,如果数据变化,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间的通信桥梁,作用:
1.在自身实例化的时候忘订阅器内添加自己
2.自身要有一个update()方法
3.等待属性变动时,调用自身的update方法,触发compile这种的回调
第四步:MVVM作为数据绑定的入口,整合了observer、compile和watcher三者,通过observer来监听自己的数据变化,通过compile解析模板指令,最后利用watcher把observer和compile联系起来,最终达到数据更新视图更新,视图更新数据更新的效果
虚拟DOM,描述元素和元素之间的关系,创建一个JS对象
如果组件内有响应的数据,数据发生改变的时候,render函数会生成一个新的虚拟DOM,这个新的虚拟DOM会和旧的虚拟DOM进行比对,找到需要修改的虚拟DOM内容,然后去对应的真实DOM中修改
diff算法就是虚拟DOM的比对时用的,返回一个patch对象,这个对象的作用就是存储两个节点不同的地方,最后用patch里记录的信息进行更新真实DOM
步骤:
1.JS对象表示真实的DOM结构,要生成一个虚拟DOM,再用虚拟DOM构建一个真实DOM树,渲染到页面
2.状态改变生成新的虚拟DOM,跟就得虚拟DOM进行比对,这个比对的过程就是DIFF算法,利用patch记录差异
3.把记录的差异用在第一个虚拟DOM生成的真实DOM上,视图就更新了。
1.原理不同
vue就是数据绑定;jq是先获取dom再处理
2.着重点不同
vue是数据驱动,jq是着重于页面
3.操作不同
4.未来发展不同
vuex是vue的状态管理工具
vue中可以直接触发methods中的方法,vuex是不可以的。未来处理异步,当触发事件的时候,会通过dispatch来访问actions中的方法,actions中的commit会触发mutations中的方法从而修改state里的值,通过getter把数据更新到视图
Vue.use(vuex),调用install方法,通过applyMixin(vue)在任意组件内执行this.$store就可以访问到store对象。
vuex的state是响应式的,借助的就是vue的data,把state存到vue实例组件的data中
1.普通遍历,对象.forEach()
arr.forEach(function(item,index,arr){
console.log(item,index)
})
2.对元素统一操作 对象.map()
var newarr = arr.map(function(item){
return item+1
})
3.查找符合条件的元素 对象.filter()
arr.filter(function(item){
if(item > 2){
return false
}else{
return true
}
})
4.查询符合条件的元素,返回索引 对象.findindex()
arr.finindex(function(item){
if(item>1){
return true
}else{
return false
}
})
对象.evening() 遇到不符合的对象会停止
对象.some() 找到符合条件的元素就停止
下载:node cnpm webpack vue-cli
创建项目:
1.找到对应的文件,然后利用node指令创建(cmd)
2.vue init webpack xxxx
3.回车项目描述
4.作者回车
5.选择vue build
6.回车
7.输入n
8.不按照yarn
9.输入npm run dev
1.使用Vue.extend()创建一个组件
2.使用Vue.components()方法注册组件
3.如果子组件需要数据,可以在props中接收定义
4.子组件修改好数据,要把数据传递给父组件,可以用emit()方法
原则:
把功能拆开
尽量让组件原子化,一个组件做一件事情
容器组件管数据,展示组件管视图
1.低耦合,组件之间的依赖越小越好
2.最好从父级传入信息,不要在公共组件中请求数据
3.传入的数据要进行校验
4.处理事件的方法写在父组件中
vue的特性,用来对文本进行格式化处理
使用它的两个地方,一个是插值表达式,一个是v-bind
分类:
1.全局过滤器
Vue.filter('add',function(v){
return v < 10 ? '0' + v : v
})
{{33 | add}}
2.本地过滤器
和methods同级
filter:{
add:function(v){
return v < 10 ? '0' + v : v
}
}
1.localtion.reload()
2.this.$router.go(0)
3.provide和inject
可以重复使用的vue实例,独一无二的组件名称
可以抽离单独的公共模块
提高代码的复用率
public
图标、index.html、img
src
api
assets
components
按分类再次划分子目录
plugins
router
static
styles
utils
views
App.vue
main.js
package.json
vue.config.js
是基于vue的应用框架,关注的是渲染,可以开发服务端渲染应用的配置
SSR:服务端渲染
好处:
SSR生成的是有内容的HTML页面,有利于搜索引擎的搜索
优化了首屏加载时间
SEO:优化搜索引擎
SPA的应用不利于搜索引擎SEO的操作
1.SSR
2.预渲染 prerender-spa-plugin