一、window对象与document对象的区别与联系
1、window对象:
概念:是指浏览器打开的窗口。
常见属性:document对象(只读)、location对象、navigator对象(只读)、closed(返回窗口是否关闭 布尔值)、screen系列(screenTop\screenLeft\screenX\screenY)、innerWidth、innerHeight等
常见方法:alert、prompt、blur、focus、print、confirm、scrollTo、moveTo、setTimeout、setInterval、clearTimeout、clearInterval
2、document对象:
概念:载入浏览器的html都会成为一个document对象。(document对象是window对象的一部分,可以在浏览器中window.document展示,由于window是全局变量,所以可以直接打印document)
常用属性:cookie\domain返回当前文档的域名\url\title\lastModified文档最后被修改的日期时间
常用方法:close()\open()\getElementById()\getElementByName\getElementByTagName\write()向文档写html或者js代码
CSS
1、css的盒模型和定位
css盒模型分为:标准盒模型(w3c)和IE盒模型。
区别标准盒模型的width和height是针对content的,IE盒模型的宽高是针对border的。
相同点都具有content\padding\border\margin
切换方法:box-sizing:content-box/border-box
2、定位
position:fixed/relative/absolute/static 绝对定位(针对浏览器)/绝对定位(针对不是static的最近的父元素)/相对定位(本身)/默认值(没有定位)
relative和absolute的区别如下
相同点:都是设置定位属性后,通过top、bottom、left、right进行定位操作;可以使用z-index进行层次分级
不同点:a、 relative是针对本身静态位置的定位,absolute是针对距离最近的具有定位属性(不是static)的父元素的位置进行定位,如没有就是针对body的定位
b、相对定位不脱离文档流,绝对定位脱离文档流
3、浮动
产生原因:子元素设置样式float:left/right,父元素的内容不会被撑开导致背景、border、margin无法显示
清除浮动方法:
a、作用于子元素的最后添加一个新元素 clear:both; height:0; line-height:0
b、作用于父元素:overflow:hidden/auto;zoom:1
c、作用于子元素,类似于方法a,使用伪类替代最后一个元素 :after{ clear:both; content:'.'; display: block; width: 0; height:0; visibility:hidden}
4、如何保持浮层水平垂直居中
方法一:定位方法
.parent{
position: relative;
width: 200px;
height:200px;
}
.child{
position: absolute;
width: 50px;
height:50px;
left: 50%;
top: 50%;
transform: transilate(-50%,-50%)
}
方法二:使用margin:0 auto;
方法三:使用vertical-align:middle(平时只能使用在父元素为td或者th中,其他的如div、p元素可以使用display:table-cell激活)
.parent{
width:200px;
height:200px;
display: table-cell;
vertical-align: middle;
text-align:center;
display:table-cell
}
.child{
display:inline-block //这个保证水平居中
}
5、display的取值和含义
display:none、inline、inline-block、block、flex
a、display:none 对比 visibility:hidden 都是隐藏盒子,前者不占据文档流,后者占据
b、display:inline/block 设置元素为行内元素或者块级元素。inline的内容有多少高度就是多少,水平布局,不能设置宽高;block类似是盒子可以设置宽高,垂直布局,独占一行
c、inline-block 介于两者之间,水平布局但是可以设置宽高
d、display:flex 弹性盒布局模型,其子元素的float、clear、vertical-align失效
提问:给行内元素设置宽高
display:inline-block;position:absolute;float:left / right;后面两行代码自动将行内元素转换成inline-block
6、选择器优先级
!important>内联样式>id选择器>class类选择器=属性选择器=伪类选择器>标签选择器=伪元素选择器>通配符选择器
说明:
a、同一级别比较的是数量,不同级别在数量上没有可比性,数量多不能改变优先级
b、如果优先级相同,后来者居上
c、样式的优先级与选择器的优先级决定,与元素距离目标元素远近无关
7、弹性盒模型
display:flex
vue
一、MVVM的理解
Model:数据模型,用来进行数据处理和业务逻辑处理
view:UI组件,用来将数据用UI 的方式呈现出来
viewModel:监听数据变化和控制视图,将model和view层使用双向数据绑定连接起来,实现两者的自动同步。
优点:可以将更多精力放在业务逻辑上面,不用手动操作dom,不需要关心数据的同步问题
提问:UI中有一个li列表,它是怎么与我们的数据对应的
解答:ul与li都不能使用v-model,可以使用下面方式绑定data中的变量
<li v-for=“item in list” :key="item">{{item}}</li>
二、vue双向数据绑定原理
https://www.jianshu.com/p/3e6b89d7d7ad
一句话总结:数据劫持 结合 发布者-订阅者模式(
发布者-订阅者模式:当数据变动时,发布消息给订阅者
)
observer:监听器,进行数据劫持(Object.defineProperty() 劫持各个属性的getter和setter),如有变化通知订阅者
watcher:订阅者,当有属性变化的通知就执行相应的更新函数,更新视图(消息订阅器Dep来专门收集这些订阅者)
compile:解析器,扫描解析各个节点的指令,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数
综上所述:
数据劫持和发布者-订阅者模式实现的双向数据绑定。重点是使用了Object.defineProperty()方法劫持数据,参数分别为对象,属性名,属性的特性。打印出对象可以看到属性特性中有set和get函数,对应的setter和getter访问器属性,分别用于设置和获取属性值。每个属性分配一个订阅者集合的管理数组dep订阅器,当获取属性值时触发getter,可以用于在编译时为dep中添加订阅者(watcher自身触发getter)。当input的监听事件发现改变属性值时触发setter,可以用于触发订阅者自身的update方法更新视图。
第一步:
需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
详细解说:
observer
数据劫持:使用Object.defineProperty()方法
接受三个参数:目标对象,目标对象的属性,目标对象的属性的特性
返回值:目标对象
对象有两种属性:数据属性和访问器属性,下图为打印出的obj={},get和set对应的函数就是访问器属性,分别用来获取属性和设置属性。
const obj={};
Object.defineProperty(obj,'name',{
get:function () { //getter:属性被获取的时候触发
console.log('obj的name属性被获取了')
return obj
} ,
set:function (newVal) { //setter:属性被赋值的时候触发,只要数据变化了就会触发该函数,所以更新的方法放在这里
console.log('obj的name属性重置为'+newVal);
} });
obj.name = 'fanym';
const name = obj.name;
console.log(name);
运行结果:
obj的name属性重置为fanym
obj的name属性被获取了
实现一个简单的双向数据绑定
效果:
代码:
<div>
<input type="text" id="txt">
<p id="show"></p>
</div>
const obj = {}
Object.defineProperty(obj,'txt',{
get:function () {
return obj
},
set:function (newVal) {
document.getElementById('txt').value = newVal;
document.getElementById('show').innerHTML = newVal;
}
});
document.addEventListener('keyup',function (e) {
obj.txt = e.target.value
});
(视图变化–》数据变化) 实现方法:给视图添加一个监听函数
(数据变化–》视图变化) 实现方法:在setter访问器属性中添加更新操作
监视器observer:
function defineReactive(data, key, val) { //对象、属性名、属性值
observe(val); // 递归遍历所有子属性
Object.defineProperty(data, key, { //监听属性变化
enumerable: true, //可枚举
configurable: true,
get: function() {
return val;
},
set: function(newVal) {
val = newVal;
console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
}
});
}
function observe(data) { // 递归遍历所有子属性
if (!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
};
示例:
var library = {
book1: {
name: ''
},
book2: ''
};
observe(library);
library.book1.name = 'vue权威指南'; // 属性name已经被监听了,现在值为:“vue权威指南”
library.book2 = '没有此书籍'; // 属性book2已经被监听了,现在值为:“没有此书籍”
订阅器Dep负责收集订阅者,然后在属性变化的时候执行对应订阅者的更新函数。改造Observer,植入消息订阅器
function defineReactive(data, key, val) {
observe(val);
var dep = new Dep(); //构造订阅器
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
if (是否需要添加订阅者) {
dep.addSub(watcher); // 在这里添加一个订阅者
}
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
dep.notify(); // 如果数据变化,通知所有订阅者
}
});
}
//设置Dep订阅器的构造函数,dep相当于一个容器,使用list包裹所有的订阅者
function Dep () {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) { //添加订阅者函数
this.subs.push(sub);
},
notify: function() { //更新函数
this.subs.forEach(function(sub) {
sub.update();
});
}
};
订阅者watcher:
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm; //data对象
this.exp = exp; //属性
this.value = this.get(); // 将自己添加到订阅器的操作
}
Watcher.prototype = {
update: function() {
this.run();
},
run: function() {
var value = this.vm.data[this.exp];
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
},
get: function() {
Dep.target = this; // 缓存自己
var value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
return value;
}
};
function defineReactive(data, key, val) {
observe(val); // 递归遍历所有子属性
var dep = new Dep();
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
if (Dep.target) { // 判断是否需要添加订阅者
dep.addSub(Dep.target); // 在这里添加一个订阅者
}
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
dep.notify(); // 如果数据变化,通知所有订阅者
}
});
}
Dep.target = null;
三、vue的生命周期
8个阶段:创建前后、挂载前后、更新前后、销毁前后
对应的钩子函数:beforeCreate/created,beforeMount/mounted,beforeUpdate/updated,beforeDestroy/destroyed
流程:
1、创建vue实例,new Vue()
2、创建实例的时候,执行init()初始化,首先会调用beforeCreate,this指向vm实例,此时method,data,watch,computed不能使用;添加loading,在加载实例时触发
3、监听data数据,初始化vue内部事件,进行属性和方法的计算
4、实例创建完成后调用created,此时data,computed、watch、methods中的方法都可用,
e
l
还
不
能
访
问
,
el还不能访问,
el还不能访问,refs属性内容为空数组;此处可以进行ajax调用,结束loading事件
5、模板编译:将data对象里面的数据和vue语法写的模板编译成html(查找el挂载元素,如果没有调用this.$mount(el),如果有查找实例内部是否有template属性,如果有直接调用,没有的话调用外部html,否则报错)
6、模板编译完成调用beforeMount
7、render函数执行生成虚拟dom,挂载到真实dom上
8、挂载后调用mounted;处理dom
9、数据变化调用beforeUpdate,经理虚拟dom,更新完成后调用updated
10组件销毁前后调用beforeDestroy、destroyed
提问:
1、初始化组件,执行的钩子函数
beforeCreate/Created/beforeMount/mounted
四、vue原理之渲染函数
vue实例创建之后,开始模板(template中的html)编译成ast(抽象语法树),render函数将ast转换成虚拟dom,并通过Patch渲染到真实dom上
渲染过程中有数据变化时,每个组件对应的watcher订阅层会根据数据依赖触发setter函数来更新视图,采用的是diff算法,根据最小的变动进行更新,这里涉及的就是vue响应式即双向数据绑定
五、Vue的性能优化
六、Vuex
vuex是vue的状态管理,vuex中包含三部分:state、mutations、actions、getter,下面一一说明:
state:一般异步请求的数据放在这里,用于存储数据
mutation:参数1是state,参数2是传递的数据,对数据进行处理并赋值。根据某个actionType更改对应的state中的某个值
action:页面view更改后监听函数触发action,在此处可以进行异步操作,拿到更改的数据,并根据actionType通知mutation进行更改
getters:将所有的数据暴露出来,可以在任何组件进行取用
注意:mapGetters位于computed中,相当于解构赋值,与mapState的区别:源值,直接从state中映射过来的。
mapAction位于methods中,当有更改调用对应的action进行操作,如果没有action可以使用this.$store.commit(‘actionType’,data)通知mutation进行修改state
七、父子组件传值
父传子:在父页面中引用子组件的标签上绑定父组件的属性值,在子组件中的props中可以获取这个属性值
父:<blog-post :title='title' />
子:export default {
props: {
title: {
type: String,
default: 'hello world'
}
}
}
子传父:在子组件中this.$emit(‘方法名’,data),在父组件引用子标签中<child @方法名=‘parent’ />,在parent方法的参数就是传递过来的数据
<!-- 父组件 -->
<template>
<div class="test">
<test-com @childFn="parentFn"></test-com>
<br/>
子组件传来的值 : {{message}}
</div>
</template>
<script>
export default {
// ...
data: {
message: ''
},
methods: {
parentFn(payload) {
this.message = payload;
}
}
}
</script>
<!-- 子组件 -->
<template>
<div class="testCom">
<input type="text" v-model="message" />
<button @click="click">Send</button>
</div>
</template>
<script>
export default {
// ...
data() {
return {
// 默认
message: '我是来自子组件的消息'
}
},
methods: {
click() {
this.$emit('childFn', this.message);
}
}
}
</script>
js
1、css的line-height:1.5,line-height:150%,line-height:1.5em,line-height:15px的区别
最近子元素行高的1.5倍
100%=1em 150%相当于1.5em
1em=16px
2、闭包
function f1(){
var n = 123;
function f2(){ //f2是一个闭包
alert(n)
}
return f2;
}
应用:在函数外部可以拿到函数内部的变量,即使函数调用后寿命终结了也可以,
概念:
js链式作用域
子对象会一级一级向上寻找所有父对象的变量,反之不行。
js变量两种作用域?
全局变量、局部变量(函数内):js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。
3、浏览器兼容问题
css样式方面:
(1)内外补丁:*{padding:0,margin:0}
(2)hack的使用:ie6使用下划线_ 和星号 *,ie7使用星号 *
(3)几个img标签放在一起的时候,有些浏览器会有默认的间距:使用float进行布局
(4)块级元素float+横向的margin,margin值会变大:设置display:inline
js方面:
(1)事件绑定:
IE:dom.attachEvent();
其他浏览器:dom.addEventListener();
// 获取浏览器可视窗口的宽度与高度:
var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
// 获取事件源元素:
// 备注:event是事件函数的参数。
const event = event || window.event;
const target = event.srcElement || event.target;
// 请注意:下方的处理都默认event已经按照上面的写法兼容过衍生的
// 阻止事件冒泡和浏览器默认行为
event.preventDefault
? event.preventDefault()
: event.returnValue = false;
// 只阻止事件冒泡
event.stopPropagation
? event.stopPropagation();
: event.cancelBubble = true;
// 注册和解除事件监听器的兼容写法
const EventListen = {
addEvent(ele,fn,str) {
//通过判断调用的方式兼容IE678
//判断浏览器是否支持该方法,如果支持那么调用,如果不支持换其他方法
if(ele.addEventListener){ //火狐谷歌IE9+支持addEventListener
//直接调用
ele.addEventListener(str,fn);
}else if(ele.attachEvent){ //IE678支持attachEvent
ele.attachEvent("on"+str,fn);
}else{
//在addEventListener和attachEvent都不存在的情况下,用此代码
ele["on"+str] = fn;
}
},
removeEvent (ele,fn,str) {
if(ele.removeEventListener){
ele.removeEventListener(str,fn);
}else if(ele.detachEvent){
ele.detachEvent("on"+str,fn);
}else{
ele["on"+str] = null;
}
}
};
EventListen.addEvent(element, function() {}, 'click');
4、事件冒泡和捕获
addEventListener(‘事件名’,“”,false)==三参是false对应的maopao
两种方式来阻止事件冒泡。
方式一:event.stopPropagation();
方式二:return false;
区别:return false 不仅阻止了事件往上冒泡,而且阻止了事件本身。event.stopPropagation() 则只阻止事件往上冒泡,不阻止事件本身。
react
一、