行内元素:span、img、input...
块级元素:div、footer、header、section、p、h1...h6...
空元素:br、hr...
元素之间的转换问题:
display: inline; 把某元素转换成了行内元素 ===>不独占一行的,并且不能设置宽高
display: inline-block; 把某元素转换成了行内块元素 ===>不独占一行的,可以设置宽高
display: block; 把某元素转换成了块元素 ===>独占一行,并且可以设置宽高
区别一: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;
}
方式一:
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%);
}
none 隐藏元素
block 把某某元素转换成块元素
inline 把某某元素转换成内联元素
inline-block 把某某元素转换成行内块元素
BFC就是页面上一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
1. 了解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
.container {
display: grid;
grid-template-columns: 300px 1fr;
}/*或者如下*/
.container {
display: flex;
}
.left {
flex-basis: 300px;
flex-shrink: 0;
}
.main {
flex-grow: 1;
}
双飞翼布局
#left{
float: left;
margin-left: -100%;
}
#right{
float: left;
margin-left: -200px;
}
.middle-inner{
margin: 0 200px;
}
reset.css 是一个css文件,用来重置css样式的。
normalize.css 为了增强跨浏览器渲染的一致性,一个CSS 重置样式库。
1. 是什么
把多个小图标合并成一张大图片。
2. 优缺点
优点:减少了http请求的次数,提升了性能。
缺点:维护比较差(例如图片位置进行修改或者内容宽高修改)
1. 占用位置的区别
display: none; 是不占用位置的
visibility: hidden; 虽然隐藏了,但是占用位置
2. 重绘和回流的问题
visibility: hidden; 、 display: none; 产生重绘
display: none; 还会产生一次回流
产生回流一定会造成重绘,但是重绘不一定会造成回流。
产生回流的情况:改变元素的位置(left、top...)、显示隐藏元素....
产生重绘的情况:样式改变、换皮肤
共同性:实现透明效果
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(String Number Date RegExp Error Function Map Set)
NaN是一个数值类型,但是不是一个具体的数字。
考题一:
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. 事件循环中包含:【微任务、宏任务】
微任务:promise.then
宏任务:setTimeout..(负责的对象是Window)
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务=》微任务...
1. 除了函数外,js是没有块级作用域。JS中作用域有:全局作用域、函数作用域。没有块作用域的概念
ES6)中新增了块级作用域。 块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
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();
考题二:
var name = 'a';
(function(){
if( typeof name == 'undefined' ){
var name = 'b';
console.log('111'+name);
}else{
console.log('222'+name);
}
})()
考题三:
function fun( a ){
var a = 10;
function a(){}
console.log( a );
}
fun( 100 );
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';
a[c] = '456';
console.log( a[b] ); // 456
考题一:
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();
考题三:
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
方式一:isArray
var arr = [1,2,3];
console.log( Array.isArray( arr ) );
方式二:instanceof 【可写,可不写】
var arr = [1,2,3];
console.log( arr instanceof Array );
方式三:原型prototype
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );
方式四:isPrototypeOf()
var arr = [1,2,3];
console.log( Array.prototype.isPrototypeOf(arr) )
方式五:constructor
var arr = [1,2,3];
console.log( arr.constructor.toString().indexOf('Array') > -1 )
1. slice是来截取的
参数可以写slice(3)、slice(1,3)、slice(-3)
返回的是一个新的数组
2. splice 功能有:插入、删除、替换
返回:删除的元素
该方法会改变原数组
方式一:new set
var arr1 = [1,2,3,2,4,1];
function unique(arr){
return [...new Set(arr)]
}
console.log( unique(arr1) );
方式二:indexOf
var arr2 = [1,2,3,2,4,1];
function unique( arr ){
var brr = [];
for( var i=0;i<arr.length;i++){
if( brr.indexOf(arr[i]) == -1 ){
brr.push( arr[i] );
}
}
return brr;
}
console.log( unique(arr2) );
方式三:sort
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]
]));//[5, 27, 39, 1001]
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld
String.prototype.addPrefix = function(str){
return str + this;
}
console.log( 'world'.addPrefix('hello') )
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){
var char = str.charAt(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);
}
}
1. 创建了一个空的对象
2. 将空对象的原型,指向于构造函数的原型
3. 将空对象作为构造函数的上下文(改变this指向)
4. 对构造函数有返回值的处理判断
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. 原型链
4.1 是什么?:就是把原型串联起来
4.2 原型链的最顶端是null
方式一:ES6
class Parent{
constructor(){
this.age = 18;
}
}
class Child extends Parent{
constructor(){
super();
this.name = '张三';
}
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );
方式二:原型链继承
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 );
方式四:组合式继承
function Parent(){
this.age = 100;
}
function Child(){
Parent.call(this);
this.name = '张三'
}
Child.prototype = new Parent();
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 ){
if( Array.isArray(obj) ){
var newObj = [];
}else{
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之后
作用:清除浮动、样式布局上也有作用
-webkit-transform
Chrome默认字体大小是:16px
**每个浏览器默认字体大小可能都不一样
<style type="text/css">
div{
font-size:10px;
}
div span{
display: inline-block;
-webkit-transform:scale(1.6);
}
</style>
相对于font-size
em针对于父元素的font-size
rem针对于根(html)元素的font-size
禁止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;
}
**布局特点:**屏幕分辨率变化时,页面里面元素的位置会变化,而大小不会变化。
1.淘宝无限适配【移动端】:淘宝无限适配 + 布局单位使用rem
2.根据屏幕大小不同引入不同的css样式文件
3.高度自适应
4.宽度自适应:宽度自适应有三种方法,分别是
1)利用绝对定位来设置宽度自适应布局
2)利用margin,中间模块先渲染来设置宽度自适应布局
3)利用自身浮动来设置宽度自适应布局
**布局特点:**每个屏幕分辨率下面会有一个布局样式,即元素位置和大小都会变。
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的尺寸做为基准点。
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只在箭头函数定义时就决定的,而且不可修改的(call、apply、bind)
****箭头函数的this指向定义时候、外层第一个普通函数的this
2. 箭头函数不能new(不能当作构造函数)
3. 箭头函数prototype
4. 箭头函数arguments
有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)
区别一:返回的内容不同
filter 返回是新数组
find 返回具体的内容
区别二:
find :匹配到第一个即返回
filter : 返回整体(没一个匹配到的都返回)
some ==》 如果有一项匹配则返回true
every ==》 全部匹配才会返回true
Webpack 是一个项目打包工具。可以压缩代码和图片,把浏览器识别不了的代码转化为能识别的,可以启动一个热加载服务器
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
具体为:
初始化参数
:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数开始编译
:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译确定入口
:根据配置中的 entry 找出所有的入口文件编译模块
:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理完成模块编译
:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系输出资源
:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会输出完成
:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统在以上过程中,Webpack
会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
webpack-dashboard
:可以更友好的展示相关打包信息。webpack-merge
:提取公共配置,减少重复配置代码speed-measure-webpack-plugin
:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。size-plugin
:监控资源体积变化,尽早发现问题HotModuleReplacementPlugin
:模块热替换高版本
的 Webpack 和 Node.js多进程/多实例构建
:HappyPack(不维护了)、thread-loader
压缩代码
图片压缩
缩小打包作用域
:
提取页面公共资源
:
DLL
:
充分利用缓存提升二次构建速度
:
Tree shaking
Scope hoisting
作用域提升
动态Polyfill
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点
1)使用我们指定目录作为Git仓库。git init newrepo
2)添加新文件。可以使用add命令添加文件。git add filename
3)提交文件。能真正被保存在Git仓库。git commit -m "Adding files"
4)当我们修改了很多文件,而不想每一个都add,想commit自动来提交本地修改,我们可以使用-a标识。git commit -a -m "Changed some files"
5)删除一个文件 git rm [file name]
6)从git中删除指定文件 git rm 文件名(包括路径)
7)git clone git://github.com/schacon/grit.git
从服务器上将代码给拉下来
8)看所有用户 git config --list
9)看已经被提交的 git ls-files
10)查看本地所有分支。git branch
11)查看当前状态.。git status
12)查看所有的分支。git branch -a
13)查看远程所有分支。git branch -r
14)提交并且加注释。git commit -am "init"
15)将文件给推到服务器上 git push origin master
16)显示远程库origin里的资源 git remote show origin
17)将本地库与服务器上的库进行关联。git push origin master:hb-dev
18)切换到远程dev分支 git checkout --track origin/dev
19)删除本地库develop git branch -D master develop
20)建立一个新的本地分支 dev git checkout -b dev
21)将分支dev与当前分支进行合并 git merge origin/dev
22)切换到本地dev分支 git checkout dev
23)查看远程库 git remote show
分支明细
(1)主分支(master):第一次向 git 仓库中提交更新记录时自动产生的一个分支。
(2)开发分支(develop):作为开发的分支,基于 master 分支创建。
(3)功能分支(feature):作为开发具体功能的分支,基于开发分支创建
分支命令
(1)git branch 查看分支
(2)git branch 分支名称 创建分支
(3)git checkout 分支名称 切换分支
(4)git merge 来源分支 合并分支 (备注:必须在master分支上才能合并develop分支)
(5)git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)
暂时保存更改
(1)存储临时改动:git stash
(2)恢复改动:git stash pop
开发过程中,每个人都有自己的特性分支,所以冲突发生的并不多,但如公共类的公共方法,当两人以上同时修改同一个文件,先后提交这些情况下就会报冲突的错误。
1)发生冲突,在IDE里面一般都是对比本地文件和远程分支的文件,然后把远程分支上文件的内容手工修改到本地文件,然后再提交冲突的文件。使其保证与远程分支的文件一致,这样才会消除冲突,然后再提交自己修改的部分。特别要注意下,修改本地冲突文件使其与远程仓库的文件保持一致后,需要提交后才能消除冲突,否则无法继续提交。必要时可与同事交流,消除冲突。
2)发生冲突,也可以使用命令。
通过git stash命令,把工作区的修改提交到栈区,目的是保存工作区的修改;
通过git pull命令,拉取远程分支上的代码并合并到本地分支,目的是消除冲突;
通过git stash pop命令,把保存在栈区的修改部分合并到最新的工作空间中;
如果想撤销提交到索引区的文件,可以通过git reset HEAD file来撤销;
如果想撤销提交到本地仓库的文件,可以通过git reset –soft HEAD^n恢复当前分支的版本库
到上一次提交的状态,索引区和工作空间不变更;
可以通过git reset –mixed HEAD^n恢复当前分支的版本库和索引区
至上一次提交的状态,工作区不变更;
可以通过git reset –hard HEAD^n恢复当前分支的版本库、索引区和工作空间
至上一次提交的状态。
Git | SVN |
---|---|
1. Git是一个分布式的版本控制工具 | 1. SVN 是集中版本控制工具 |
2.Git属于第3代版本控制工具 | 2.SVN属于第2代版本控制工具 |
3.客户端可以在其本地系统上克隆整个存储库 | 3.版本历史记录存储在服务器端存储库中 |
4.即使离线也可以提交 | 4.只允许在线提交 |
5.Push/pull 操作更快 | 5.Push/pull 操作较慢 |
6.工程可以用 commit 自动共享 | 6.没有任何东西自动共享 |
git pull 命令:从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。
git fetch :也用于相同的目的,但它的工作方式略有不同。当你执行 git fetch 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 git fetch 之后执行git merge。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式:
<center><h5>git pull = git fetch + git mergeh5>center>
git 使用你的用户名将提交与身份进行关联。 git config 命令可用来更改你的 git 配置,比如说用户名啥的。
假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用:
git config –global user.name “Your Name”: 此命令添加用户名。
git config –global user.email “Your E-mail Address”: 此命令添加电子邮件ID。
1. 有哪些生命周期
系统自带:
beforeCreate
created
beforeMount
mounted
beforeUpdate
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次进入组件会执行哪些生命周期?
只执行一个生命周期:activated
2.1 v-show
显示和隐藏 : display:none进行隐藏 、display:block进行显示
2.2 v-if
创建和删除:remove、append
2.3 区别:
显示和隐藏用:v-show
创建和删除用:v-if
频繁切换用:v-show
不频繁切换用:v-if
首次加载:用v-if,不用v-show
为什么:如果用v-if可以没有这个盒子,然后再通过v-if进行创建(但是第一次进入页面是没有这个盒子,是不加载的)。
如果用v-show这个盒子不管是显示还是隐藏,该盒子都是在的(节点都是存在)
2.4 使用场景:
v-show : 加入购物车、分享、蒙层这种都基本上用v-show
v-if : 首页栏目切换的时候v-if
v-for的优先级要比v-if的优先级高
证明这个事情,是在vue.js源码种10997行
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
注:v-if和v-for不要写在同一个节点上,这个性能很差。(v-if要写在父节点上)
获取dom。ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
使用场景:如果项目中使用插件,并且插件是要获取dom的,那么就可以使用ref了。
缓存组件,keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 。
一旦使用keep-alive会多两个生命周期:activated、deactivated
功能:提升性能的
作用就是能够缓存不活动的组件,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现。主要用于保留组件状态或避免重新渲染;
使用:简单页面时
缓存: < keep-alive include=”组件名”>< /keep-alive>
不缓存: < keep-alive exclude=”组件名”>< /keep-alive>
核心:当dom更新完毕,执行内部代码
nextTick()是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数, nextTick()的原理: 当调用nextTick方法时会传入两个参数,回调函数和执行回调函数的上下文环境,如果没有提供回调函数,那么将返回promise对象。
使用场景:使用插件的时候会用到。例如new Swiper这个插件可能会获取当前元素的宽度或者高度,等dom都加载完毕再去获取宽度和高度就不会有任何问题了。
computed:计算属性
methods : 可以放入函数:没有缓存,如果一进入页面调用,就会触发
watch :监听(路由和数据)、当数据发生改变时,才会触发、可以得到现在的值和过去的值
三者的共同点:
methods、computed、watch都是以函数为基础的
computed与watch都是以vue的依赖为基础,当所依赖的数据发生变化的时候会触发相关的函数去实现数据的变动
methods里是用来定义函数的,需要手动才能执行,不想computed和watch是“自动执行”函数
三者的不同点:
computed是计算属性,computed所依赖的属性发生变化是,计算属性才会重新计算,并进行缓存。当其他数据发生改变的时候computed属性不会重新计算,从而提升性能
watch:
a、watch类似于事件监听+事件机制
b、watch的方法默认是不会执行的,只有依赖的属性发生变化才会执行
c、watch默认第一次是不会执行的,通过声明immediate选项为true可以立即执行一次handler
d、watch用在监听数据变化,给后台发送数据请求
e、watch中的handler默认只能监听属性引用的变化,但内部属性是监听不到的,设置deep为true可以进行深度监听,但是性能开销也会变大
f、watch无法监听数据值的变化(特殊情况下)
computed和watch的使用场景:
computed一个数据受多个数据的影响 例如:商品购物车的结算,过滤某些商品数据
watch一个数据影响多个数据 例如:网络请求、浏览器自适应、监控路由对象
父传子
父:
<HelloWorld :msg="str" />
<HelloWorld :msg="str" ></HelloWorld>
子:
props:['msg']
props: {
msg: String,
},
子传父
子:
<button @click="changeParentName">改变父组件的name</button>
export default {
methods: {
//子组件的事件
changeParentName: function() {
this.$emit('handleChange', 'Jack') // 触发父组件中handleChange事件并传参Jack
// 注:此处事件名称与父组件中绑定的事件名称要一致
}
}
}
父:
<child @handleChange="changeName"></child>
methods: {
changeName(name) { // name形参是子组件中传入的值Jack
this.name = name
}
}
兄弟组件传值
创建bus作为中转
import Vue from "vue";
export default new Vue;
A组件:
<button @click='btn'>HelloWorld按钮</button>
data () {
return {
hlStr:"这是helloWorld组件的数据"
}
},
methods:{
btn(){
bus.$emit('selectItem',this.hlStr);
}
}
B组件:
created(){
bus.$on('selectItem',(val)=>{
console.log( val , 1111);
})
}
理解为一个占位符,当需要用时可以定义一个slot,不需要用时slot,页面上也不会有痕迹。 插槽显不显示是由父盒子决定的,插槽在哪个位置上显示是由子组件决定的。
使用场景:组件中有些地方的布局可能大多一致,但是细微有些小小变化。
什么是路由:根据不同的url地址展示不同的页面或者数据
前端路由的理解:a、前端路由用于单页面开发,SPA。b、前端路由是不涉及到服务器的,是前端利用hash或者h5的historyAPI来实现的,一般用于不同的内容展示和切换
前端路由:把不同路由对应不同的内容或页面的任务交给前端来做,之前是通过服务端根据 url 的不同返回不同的页面实现的。在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。
//index.js配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../component/Home'
//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter)
//2.创建VueRouter对象
const router = new VueRouter({
routes,
mode: 'history' //默认是hash模式,这样设置后会设置成history模式
})
const routes = [
{
path: '/',
redirect: '/home'
},
{
path:'/home',
component: Home
}
]
//3.将router对象传入到Vue实例
export default router
--------------------------------------------------------------
//App.vue文件中挂载
首页 //会被渲染成a标签
//占位
通过代码修改路由
this.$router.push('/home')
this.$router.replace('/home')
路由懒加载
const Home = () => import('../components/Home')
①a标签进行跳转 首页
②router-link进行跳转 首页
③编程式路由
1、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面
2、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面
3、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
显示:
传:
this.$router.push({
path:'/about',
query:{
key:'你好'
}
})
接:
this.$route.query
隐示:
传:
this.$router.push({
name:'About',
params:{
key:'你好'
}
})
接:
this.$route.params
mode: "history" http://localhost:8080/about
mode:"hash" http://localhost:8080/#/about
全局
beforeEach
beforeResolve
afterEach
路由独享
beforeEnter
组件内
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
场景:要去拦截,判断用户是否是登录状态。功能:进入地址管理,用户如果没有登录是进入不了地址管理(在进入之前判断拦截),需要先登录。
子路由:children
动态路由:path: '/user/:id'
通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{ str }}),从而实现了数据双向绑定的原理。
功能:提升性能
虚拟dom ===》其实就是数据( 把dom数据化 )
snabbdom:https://www.npmjs.com/package/snabbdom
npm init -y
cnpm install webpack@5 webpack-cli@3 webpack-dev-server@3 -S
cnpm install snabbdom -S
新建webpack.config.js
配置webpack.config.js
虚拟节点:
{
children: undefined
data: {}
elm: h1
key: undefined
sel: "h1"
text: "你好h1"
}
真实节点:
你好
1、如果新老节点不是同一个节点名称,那么就暴力删除旧的节点,创建插入新的节点。
2、只能同级比较,不能跨层比较。如果跨层那么就暴力删除旧的节点,创建插入新的节点。
3、如果是相同节点,又分为很多情况
3.1 新节点有没有children
如果新的节点没有children,那就证明新节点是文本,那直接把旧的替换成新的文本
3.2 新节点有children
新的有children,旧的也有children ===》就是diff算法的核心了【3.3】
新的有children,旧的没有 ===》创建元素添加(把旧的内容删除清空掉,增加新的)
3.3 diff算法的核心(最复杂的情况)
1、 旧前 和 新前
匹配:旧前的指针++ 、 新前的指针++
2、 旧后 和 新后
匹配:旧后的指针-- 、 新后的指针--
3、 旧前 和 新后
匹配:旧前的指针++ 、 新后的指针--
4、 旧后 和 新前
匹配:旧后的指针-- 、 新前的指针++
5、 以上都不满足条件 ===》查找
新的指针++,新的添加到页面上并且新在旧的种有,要给旧的复制成undefined
6、 创建或者删除
***注意:如果要提升性能,一定要加入key,key是唯一标示,在更改前后,确认是不是同一个节点。
MVVM分为Model、View、ViewModel三者。
Model 代表数据模型,数据和业务逻辑都在Model层中定义;View 代表UI视图,负责数据的展示;ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。
MVVM和MVC都是一种设计思想,主要就是MVC中的Controller演变成ViewModel,,MVVM主要通过数据来显示视图层而不是操作节点,解决了MVC中大量的DOM操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。
场景:数据操作比较多的场景,更加便捷。
传统的 MVC 架构通常是使用控制器更新模型,视图从模型中获取数据去渲染。当用户有输入时,会通过控制器去更新模型,并且通知视图进行更新,但MVC 有一个巨大的缺陷就是随着项目功能越多、项目越加复杂,控制器中的代码会越来越多,不利于维护。
在 MVVM 架构中,引入了 ViewModel 的概念。ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种情况下,View和 Model 都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。
scoped属性:持在标签添加
scoped
属性,这样当前组件的样式只会在当前样式生效,其它组件不会影响到。
通过深度选择器来样式穿透的。除了上面的讲/deep/
,还有::v-deep
,>>>
scoped
是 style 标签的一个属性,当在 style 标签中定义了 scoped 时,style 标签中的所有属性就只作用于当前组件的样式,实现组件样式私有化,从而也就不会造成样式全局污染
是单向数据流
单向数据流(Unidirectional data flow)方式使用一个上传数据流和一个下传数据流进行双向数据通信,两个数据流之间相互独立
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)
Model 改变 View的过程: 依赖于ES5的object.defindeProperty,通过 defineProperty 实现的数据劫持,getter 收集依赖,setter 调用更新回调(不同于观察者模式,是发布订阅 + 中介) View 改变 Model的过程: 依赖于 v-model ,该语法糖实现是在单向数据绑定的基础上,增加事件监听并赋值给对应的Model。
1、什么是虚拟DOM
虚拟DOM就是用来模拟DOM结构的一个js对象。
2、虚拟 DOM 的简单实现原理主要包括以下 3 部分:
1)用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
2)diff 算法 — 比较两棵虚拟 DOM 树的差异;
3)pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
3、虚拟DOM的优点和缺点
1. 优点:1). 保证性能下限
2). 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,然后根据我们所写的代码去更新视图
3). 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,但是 DOM 与平台的相关性是非常强的,相比之下虚拟 DOM 可以进行更方便地跨平台操作
2. 缺点:1). 首次显示要慢些:首次渲染大量DOM时,由于多了一层虚拟DOM的计算, 会比innerHTML插入慢
2). 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中无法进行针对性的极致优化。
SPA( single page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;是会利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。缺点:SEO优化不好;性能不是特别好
1、优点:
1). 用户体验好、速度快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
2). 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
3). 基于上面一点,SPA 相对对服务器压力小;
2、缺点:
1). 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
2). 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
3). SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
vue中有2种数据绑定的方式:单向数据绑定(v-bind):数据只能从data流向页面;
双向数据绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data;
备注:双向数据绑定一般都应用在表单类(输入类)元素上 (如:input、select等);
v-model:value可以简写为v-model,因为v-model默认收集的就是value的值;
优先级由高到低:
props ==> methods ==> data ==> computed ==> watch
这是其源码中体现的。
vuex是一个状态管理工具,所谓状态的是就是数据,采用集中式存储管所有组件的状态,是为了结局中大型项目一个数据共享的问题。vuex 他可以将数据保存到本地,数据是响应式的,能够保持数据页面的共享,提高开发效率。
答:State、 Getter、Mutation 、Action、 Module。
state:vuex的基本数据,数据源存放地,用于定义共享的数据。
getter:从基本数据派生的数据,相当于state的计算属性
mutation:提交更新数据的方法,唯一 一个可以操作state 中数据的方法,必须是同步的,第一个参数是state,第二个参数是cmmi传过来的数据
action:action是用来做异步操作的,一般用来发请求,在 action 中写入函数,然后在页面中用dispatch调用,然后在 action 中通过commit 去调用 mutation 通过 mutation 去操作state。
modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
当组件进行数据修改的时候,我们需要调用dispatch来触发actions里面的方法。
actions里面的每个方法中都会有一个commit方法,当方法执行的时候会通过commit来触发mutations里面的方法进行数据的修改。
mutations方法里面的每个函数都会有一个state参数,这样就可以在mutations里面进行state的数据修改,当数据修改完毕后,会传导给页面。页面的数据也会发生改变。
共享、方便管理、方便维护、组件传值…
项目:购物车数据,订单数据,用户的登录信息…
vue框架中状态管理。在main.js引入store,注入。新建了一个目录store,…… export 。
场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车。
答:① 可维护性下降,修改数据,需要维护好个地方② 可读性下降,组件内的数据来源不明确③增加耦合,Vue用Component的初衷在于降低耦合性,如果大量的上传分发,反而会增加耦合度。
好处:能够在 vuex 中集中管理共享的数据,易于开发和后期维护 可以做状态管理、采用localstorage保存信息、数据一直存储在用户的客户端中 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步,能够高效地实现组件之间的数据共享,提高开发效率。
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
const store = new Vuex.Store({
// 开启严格模式,仅需在创建 store 的时候传入 strict: true:
strict: true})
本质区别:
mutations 必须是同步函数
actions “可以包含”任意异步操作
使用区别:mutations中可以放入函数,actions也可以放入函数,但是一般我们在mutations中放入函数而actions是提交mutations
具体区别:
- action 提交的是 mutation,而不是直接变更状态。mutation可以直接变更状态。
- action 可以包含任意异步操作。mutation只能是同步操作。提交方式不同,action 是用`this.$store.dispatch('ACTION_NAME',data)`来提交。mutation是用`this.$store.commit('SET_NUMBER',10)`来提交。
- 接收参数不同,mutation第一个参数是state,而action第一个参数是context,其包含了state、rootState、commit、dispatch、getters、rootGetters。
五个阶段:JavaScript执行、样式计算、页面布局、绘制和合成。
随着前端技术的迭代、业务复杂度的加深,我们所要面对的性能问题是很难罗列穷尽的。面对更复杂的性能问题场景时,我们应该学会熟练使用浏览器的开发者工具,去分析出可能存在的性能瓶颈并定位到问题元素的位置,然后制定出合理的优化方案进行性能改进。应当做到所有性能优化都要量化可控,避免盲目地为了优化而优化,否则很容易画蛇添足。
主要两个方向:资源加载优化 和 页面渲染优化。
①路由懒加载②服务端渲染③骨架屏注入
1、将公用的JS库通过script标签外部引入,减小app.bundel的大小,让浏览器并行下载资源文件,提高下载速度;
2、在配置路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
3、加一个首屏 loading 图,提升用户体验;
1.利用各种前端利器辅助查找问题根源 —Firebug,IE WebDeveloper等等。
2.边框背景调试法 —估计一下出错的位置在那,在它的标签上加边框或背景,这样就可以看到该模块占了多少位置,是否在撑出规定的范畴。
3.最大宽度最小宽度法 —找到出错区块修改出题的模块的宽度,如果宽度改小后,下沉问题解决。就可以判断问题就是出现在这个元素上。
4.样式排除法 —有些复杂的页面也许加载了 N 个外链 CSS 文件,那么逐个删除 CSS 文件,找到 BUG 触发的具体 CSS 文件,缩小锁定的范围。
5.模块确认法 —有时候我们也可以从页面的 HTML 元素出发。删除页面中不同的 HTML 模块,寻找到触发问题的 HTML 模块。
6.至于其它问题的,最好的办法就是google,百度,看书。对于这些方法还不行,可以问有经验的同事帮忙。
1.H5新标签在IE9以下的浏览器识别
2.ul标签内外边距问题ul标签在IE6\IE7中,有个默认的外边距,但是在IE8以上及其他浏览器中有个默认的内边距。
解决方法:统一设置ul的内外边距为0
1.css的hack问题:主要针对IE的不同版本,不同的浏览器的写法不同
IE的条件注释hack:
2.IE6双边距问题:IE6在浮动后,又有横向的margin,此时,该元素的外边距是其值的2倍
解决办法:display:block;
3.IE6下图片的下方有空隙
解决方法:给img设置display:block;
4.IE6下两个float之间会有个3px的bug
解决办法:给右边的元素也设置float:left;
5.IE6下没有min-width的概念,其默认的width就是min-width
6.IE6下在使用margin:0 auto;无法使其居中
解决办法:为其父容器设置text-align:center;
7.被点击过后的超链接不再具有hover和active属性
解决办法:按lvha的顺序书写css样式,
8.在使用绝对定位或者相对定位后,IE中设置z-index失效,原因是因为其元素依赖于父元素的z-index,但是父元素默认为0, 子高父低,所以不会改变显示的顺序。
9.IE6下无法设置1px的行高,原因是由其默认行高引起的
解决办法:为期设置overflow:hidden;或者line-height:1px;
1、标准的事件绑定方法函数为addEventListener,但IE下是attachEvent;
2.事件的捕获方式不一致,标准浏览器是由外至内,而IE是由内到外,但是最后的结果是将IE的标准定为标准
3.window.event获取的。并且获取目标元素的方法也不同,标准浏览器是event.target,而IE下是event.srcElement
4.在低版本的IE中获取的日期处理函数的值不是与1900的差值,但是在高版本的IE中和标准浏览器保持了一致,获取的值也是与1900的差值。
比如:var year= new Date().getYear();
5.ajax的实现方式不同,这个我所理解的是获取XMLHttpRequest的不同,IE下是activeXObject
6.IE中不能操作tr的innerHtml7.获得DOM节点的父节点、子节点的方式不同
其他浏览器:parentNode parentNode.childNodes
IE:parentElement parentElement.childre
1)出于浏览器的同源策略限制,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
2)非同源限制
1)无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
2)无法接触非同源网页的 DOM
3)无法向非同源地址发送 AJAX 请求
①服务器代理 http-proxy-middleware 原理:服务器之间的请求不存在跨域问题
②nginx proxy-pass后面跟一个跨域的地址
③cors 服务端的代理方式 设置响应头:header(Access-Control-Allow-Origin:*)
④jsonp 原理:表单中的action、script中的src、a标签和link标签中的href都会造成页面的跳转,所以这不存在跨域的问题。jsonP的原理是利用script的src进行跳转,前端将函数传到服务端,服务端将函数加工后再返回,所以jsonp和ajax不是一回事。 缺点:只支持get
使用 jsonp 来实现跨域请求,
使用 CORS 的方式
Access-Control-Allow-Origin
就可以开启 CORS。简单请求和复杂请求的区别:
使用 postMessage
websocket 协议
使用Node
Node
中间件代码(两次跨域):使用代理服务器
document.domain
location.hash
window.name
区别:
1.`HTTP` 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
2.`HTTP` 是不安全的,而 HTTPS 是安全的
3.`HTTP` 标准端口是80 ,而 HTTPS 的标准端口是443
4.`在OSI` 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
5.`HTTP` 无法加密,而HTTPS 对传输的数据进行加密,证的网络协议,安全性高于HTTP协议。
6.`HTTP`无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书,一般免费证书少,因而需要一定费用。
URL
,找到服务器,向服务器发送请求。服务器将自己的证书(包含服务器公钥)、对称加密算法种类以及其他相关信息返回给浏览器。CA
证书是否可依赖,确认证书有效。URL
一起发送给服务器。URL
连接解密。对称加密
:
对称加密 可以理解为对原始数据的可逆变换。比如 Hello 可以变换成 Ifmmp,规则就是每个字母变成它在字母表上的后一个字母,这里的秘钥就是 1
非对称加密:
XSS(Cross Site Script)跨站脚本攻击。是一种代码注入攻击
指的是攻击者向网页注入恶意的客户端代码,通过恶意的脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。
攻击
Cookie
信息。恶意 JavaScript 可以通过 document.cookie
获取 Cookie
信息,然后通过 XMLHttpRequest
或者 Fetch
加上 CORS
功能将数据发送给恶意服务器。恶意服务器拿到用户的 Cookie
信息之后,就可以在其他电脑上模拟用户的登录,然后进行转账等操作。addEventListener
接口来监听键盘事件,比如可以获取用户输入的信用卡等信息,将其发送到恶意服务器。黑客掌握了这些信息之后,又可以做很多违法的事情。DOM
伪造假的登录窗口,用来欺骗用户输入用户名和密码等信息。注入恶意脚本分 3 种方式:
标签。防御
script
和
等标签进行转义或者过滤Cookie
,只能在 HTTP 请求过程中使用 Cookie
CSRF 攻击(Cross-site request forgery)即跨站请求伪造。
是一种劫持受信任用户向服务器发送非预期请求的攻击方式,通常情况下,它是攻击者借助受害者的 Cookie 骗取服务器的信任,但是它并不能拿到 Cookie,也看不到 Cookie 的内容,它能做的就是给服务器发送请求,然后执行请求中所描述的命令,以此来改变服务器中的数据,也就是并不能窃取服务器中的数据。
CSRF 攻击就是黑客利用用户的登录状态,并通过第三方的站点来做一些坏事。
攻击
打开攻击者提供的页面后,攻击者有 3 种方式实施 CSRF 攻击:
方法一:自动发起 Get 请求
黑客将转账的请求接口隐藏在 img
标签内,欺骗浏览器这是一张图片资源。
当该页面被加载时,浏览器会自动发起 img
的资源请求,如果服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的 100 块就被转移到黑客的账户上去了。
方法二:自动发起 POST 请求
在页面中构建了一个隐藏的表单,该表单的内容就是极客时间的转账接口。
当用户打开该站点之后,这个表单会被自动执行提交;当表单被提交之后,服务器就会执行转账操作。
因此使用构建自动提交表单这种方式,就可以自动实现跨站点 POST 数据提交。
方法三:引诱用户点击链接
防御、避免方式
token
,之后每个请求都需要同时带上 token
和 Cookie
才会被认为是合法请求Referer
来验证来源站点,但请求头很容易伪造Cookie
的 SameSite
,可以让 Cookie
不随跨站请求发出,但浏览器兼容不一通过输入SQL语句,来伪造登录或者攻击网站。就是通过吧SQL命令插入到Web表单递交或输入域名,最终达到欺骗服务器执行恶意的SQL命令。
例如,输入用户名:jsliang’ OR 1 = 1 —,这样就能一直登录成功
SELECT * FROM user WHERE username='jsliang' AND psw='123456'
SELECT * FROM user WHERE username='jsliang' OR 1 = 1 --' AND psw='xxxx'
解决:表单输入时通过正则表达式将一些特殊字符进行转换
流量劫持基本分两种:DNS 劫持 和 HTTP 劫持,目的都是一样的,就是当用户访问 github.com 的时候,给你展示的并不是或者不完全是 github.com 提供的 “内容”。
DNS 劫持
DNS
劫持,也叫做域名劫持。
当用户通过某一个域名访问一个站点的时候,被篡改的 DNS
服务器返回的是一个恶意的钓鱼站点的 IP
,用户就被劫持到了恶意钓鱼站点,然后继而会被钓鱼输入各种账号密码信息,泄漏隐私。
这类劫持:
HTTP 劫持
HTTP
劫持主要是当用户访问某个站点的时候会经过运营商网络,而不法运营商和黑产勾结能够截获 HTTP
请求返回内容,并且能够篡改内容,然后再返回给用户,从而实现劫持页面。
轻则插入小广告,重则直接篡改成钓鱼网站页面骗用户隐私,就好比 jsliang 访问某 XXDN
网站,会出现 Google 广告,实际上问了其他人的是不会有这个的。
能够实施流量劫持的根本原因,是 HTTP
协议没有办法对通信对方的身份进行校验以及对数据完整性进行校验。如果能解决这个问题,则流量劫持将无法轻易发生。
预防
所以防止 HTTP
劫持的方法只有将内容加密,让劫持者无法破解篡改,这样就可以防止 HTTP
劫持了。
HTTPS
是基于 SSL
协议的安全加密网络应用层协议,相当于 HTTP
+ SSL
,可以很好地防止 HTTP
劫持。
服务器端会为每个请求创建一个链接,并向其发送确认报文,然后等待客户端进行确认。DDOS其原理就是利用大量的请求造成资源过载,导致服务不可用。
1)、DDos 攻击
客户端向服务端发送请求链接数据包;服务端向客户端发送确认数据包;客户端不向服务端发送确认数据包,服务器一直等待来自客户端的确认
2)、DDos 预防( 没有彻底根治的办法,除非不使用TCP )
限制同时打开SYN半链接的数目;缩短SYN半链接的Time out 时间;关闭不必要的服务
ICMP
包等 中间人攻击(Man-in-the-MiddleAttack,简称“MITM攻击”)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方 直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。
中间人攻击可分为如下五种不同的类型:
1)Wi-Fi欺骗:攻击者可以创建与本地免费Wi-Fi同名的虚假Wi-Fi接入点(AP)。例如,在上例的咖啡馆中,攻击者会模仿创建一个和墙上贴着Wi-Fi信息同名“Guest Wi-Fi”。一旦您连接上去,您的一切在线网络行为,将尽在攻击者的监控和掌握之中。
2)HTTPS欺骗:攻击者通过欺骗您的浏览器,使您认为自己访问的是可信任站点。当您输入与该站点相关的登录凭据时,您的流量将被重定向到攻击者自建的非安全站点处。
3)SSL劫持:通常,当您尝试连接或访问不安全的HTTP://站点时,您的浏览器会自己将您重定向到安全的HTTPS://处。此时,攻击者则可以劫持该重定向的过程,将指向其自建服务器的链接植入其中,进而窃取您的敏感数据、以及输入的所有信任凭据。
4)DNS欺骗:为了准确地浏览到目标网站,域名系统会将地址栏中的URL,从人类易于识别的文本格式,转换为计算机易于识别的IP地址。然而,DNS欺骗则会迫使您的浏览器,在攻击者的控制下,发生转换异常,而去访问那些被伪造的地址。
5)电子邮件劫持:如果攻击者获得了受信任机构(例如银行)的邮箱、甚至是邮件服务器的访问权限,那么他们就能够拦截包含敏感信息的客户电子邮件,甚至以该机构的身份发送各种电子邮件。
防御
1)确保在URL前你所访问的网站有HTTPS
2)点击电子邮件前,检查电子邮件的发件人
3)如果你是一个网站管理员,你应当执行HSTS协议
4)不要在公共Wi-Fi网络上购买或发送敏感数据
5)确保你的网站没有任何混合内容
6)如果你的网站使用了SSL,确保你禁用了不安全的SSL/TLS协议。你应当只启用了TLS 1.1和TLS 1.2
7)不要点击恶意链接或电子邮件
8)不要下载盗版内容
9)将安全工具正确地安装在系统上
session和token都是用来保持会话,功能相同
session机制,原理
**1)**session是服务端存储的一个对象,主要用来存储所有访问过该服务端的客户端的用户信息(也可以存储其他信息),从而实现保持用户会话状态。但是服务器重启时,内存会被销毁,存储的用户信息也就消失了。
2)不同的用户访问服务端的时候会在session对象中存储键值对,“键”用来存储开启这个用户信息的“钥匙”,在登录成功后,“钥匙”通过cookie返回给客户端,客户端存储为sessionId记录在cookie中。当客户端再次访问时,会默认携带cookie中的sessionId来实现会话机制。
3)session是基于cookie的
cookie的数据4k左右
cookie存储数据的格式:字符串key=value
cookie存储有效期:可以自行通过expires进行具体的日期设置,如果没设置,默认是关闭浏览器时失效。
cookie有效范围:当前域名下有效。所以session这种会话存储方式方式只适用于客户端代码和服务端代码运行在同一台服务器上(前后端项目协议、域名、端口号都一致,即在一个项目下)
**4)**session持久化
用于解决重启服务器后session就消失的问题。在数据库中存储session,而不是存储在内存中。通过包:express-mysql-session **5)**其它
当客户端存储的cookie失效后,服务端的session不会立即销毁,会有一个延时,服务端会定期清理无效session,不会造成无效数据占用存储空间的问题。
token机制,原理
适用于项目级的前后端分离(前后端代码运行在不同的服务器下)
请求登录时,token和sessionId原理相同,是对key和key对应的用户信息进行加密后的加密字符,登录成功后,会在响应主体中将{token:‘字符串’}返回给客户端。客户端通过cookie、sessionStorage、localStorage都可以进行存储。再次请求时不会默认携带,需要在请求拦截器位置给请求头中添加认证字段Authorization携带token信息,服务器端就可以通过token信息查找用户登录状态。
共同点:都是保存在浏览器端、且同源的
区别:
1、cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
2、存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
3、数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
4、作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
5、web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
6、web Storage的api接口使用更方便
cookie:一个大小不超过4K的小型文本数据,一般由服务器生成,可以设置失效时间;若没有设置时间,关闭浏览器cookie失效,若设置了 时间,cookie就会存放在硬盘里,过期才失效,每次http请求,header都携带cookie
localStorage:5M或者更大,永久有效,窗口或者浏览器关闭也会一直保存,除非手动永久清除或者js代码清除,因此用作持久数据,不参与和服务器的通信
**sessionStorage:**关闭页面或浏览器后被清除。存 放数据大小为一般为 5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信。
SEO是英文 Search Engine Optimization 的缩写,中文意思“搜索引擎优化”。SEO是指在了解搜索引擎自然排名机制的基础上,对网站进行内部及外部的调整优化,改进网站在搜索引擎中的关键词自然排名,从而获得更多流量,最终达成品牌建设或者产品销售的目的。
在了解搜索引擎自然排名机制的基础上,对网站进行内部及外部的调整优化,改进网站在搜索引擎中的关键词自然排名,从而获得更多流量,最终达成品牌建设或者产品销售的目的。简单来说就是通过一定技术手段提高网站关键词搜索排名获取更多展示,然后从自然搜索结果获得更多网站流量的过程。