前言:带*都是重点
是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图,实现数据和视图同步。
具体步骤:
第一步: 需要observer(观察者)对数据对象进行递归遍历,包括子属性对象的属性,都加上 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变更的双向绑定效果
简述MVVM和MVC
(1)MVC:是后台的框架模式
分为M:(model模型)、V(view试图)、C(controller控制器)
(2)MVVM是为了实现MVC中的V
MVVM分为:M(model数据)、V(view试图)、VM(viewModel控制数据的改变和控制试图)
想要理解虚拟dom首先要知道什么是虚拟dom?
通过js创建一个Object对象来模拟真实DOM结构,这个对象包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,通过vue中的render()函数把虚拟dom编译成真实dom,在通过appendChild()添加到页面中。
虚拟dom可以简单的用一句话概括,就是用普通的js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。
为什么要用虚拟DOM来描述真实的DOM呢?
创建真实DOM成本比较高,如果用 js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。
Diff算法
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁
• 比较只会在同层级进行, 不会跨层级比较
• 在diff比较的过程中,循环从两边向中间比较
Diff算法的步骤:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了
在父组件的子组件标签上绑定一个属性,挂载要传输的变量。在子组件中通过props来接受数据,props可以是数组也可以是对象,接受的数据可以直接使用
Bus.$off(“事件名”)
父组件调用子组件的方法通过ref
在DOM元素上使用$refs
可以迅速进行dom定位,类似于$("selectId")
使用this.$refs.paramsName能更快的获取操作子组件属性值或函数
vue2.0只允许单向数据传递,我们通过出发事件来改变组件的数据
在父组件的子组件标签上通过绑定自定义事件,接受子组件传递过来的事件。子组件通过$emit触发父组件上的自定义事件,发送参数(第一个是要改变的属性值,第二个是要发送的参数)
通过main.js初始化一个全局的$bus
,在发送事件的一方通过 b u s . bus. bus.emit(“事件名”,传递的参数信息)发送,在接收事件的一方通过$bus.$on
(“事件名”,参数)接收传递的事件
Bus.$on(“事件名”)
1.vuex :是一个专为vue.js开发的状态管理工具,采用集中式存储的所有组件状态,通过vuex我们可以解决组件之间数据共享的问题,后期也方便我们管理以及维护
有五个属性分别是: state、getters、mutations、actions、module
state属性: 存放状态,例如你要存放的数据
getters:类似于共享属性,可以通过this.$store.getters来获取存放在state里面的数据
mutations:唯一能改变state的状态就是通过提交mutations来改变,this.$store.commit()
actions: 异步的mutations,可以通过dispatch来分发从而改变state
modules:模块化管理store(仓库),每个模块拥有自己的 state、mutation、action、getters
2.基本使用:我通过是在根目录下新建一个store文件夹,里面创建一个index.js文件,最后在main.js中引入,并挂载到实例上,之后那个组件中需要用到vuex就调用就行
3.高级用法-数据持久化
vuex里面存放的数据,页面一经刷新会丢失:
解决办法: 存放在localStorage或者sessionStorage里面,进入页面时判断是否丢失,丢失再去localStorage或者sessionStorage里面取;
在vuex中可以通过安装vuex-persistedstate [pərˈsɪstɪd steɪt] 插件,进行持久化的配置就行
4.高级用法-辅助函数(语法糖)
vue中的自定义指令:
vue中除了核心功能内置的指令外,也允许注册自定义指令。自定义指令又分为全局的自定义指令和局部自定义指令。
全局自定义指令是通过Vue.directive(‘第一个参数是指令的名称’,{第二个参数是一个对象,这个对象上有钩子函数})
Vue.directive('focus', {
// el:指令所绑定的元素,可以用来直接操作 DOM。
//binding:一个对象,包含以下 property:
inserted: function (el) { vNode参数
el.focus();
}
});
局部自定义指令:
是定义在组件内部的,只能在当前组件中使用
directives: {
// 指令名称
dir1: {
inserted(el) { // inserted 表示被绑定元素插入父节点时调用
// 指令中第一个参数是当前使用指令的DOM
console.log(el);
console.log(arguments);
// 对DOM进行操作
el.style.width = '200px';
el.style.height = '200px';
el.style.background = '#000';
}
},
color: { // 为元素设置指定的字体颜色
bind(el, binding) {
el.style.color = binding.value;
}
}
}
钩子函数:
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
bind:只调用一次,指令第一次绑定到元素时调用。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数中的参数有:
el指令所绑定的元素,可以用来直接操作 DOM
binding:一个对象,里面有以下参数:
name
:指令名,不包括 v- 前缀。
value
:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
使用场景:input输入框自动聚焦使用自定义指令 v-focus,在script中使用directives注册v-focus指令
vue中自定义过滤器
• 过滤器是对即将显示的数据做进一步的筛选处理,然后显示,过滤器并没有改变原来的数据,只是在原数据的基础上产生新的数据
• 过滤器分为全局过滤器和局部过滤器
全局过滤器
全局过滤器是通过Vue.filter()来定义的,定义好后,它在所有组件中都可以使用。第一个是过滤器名称 第一个参数是需要过滤的值 第二个参数是给过滤器传递的值
// global-filter是过滤器名称
// 函数第一个参数是需要过滤的数据.
// 函数第二个参数是给过滤器传递的值.
Vue.filter('global-filter',(val,...args)=>{
console.log(`需要过滤的数据是:${val}`)
return val + ' 过滤器追加的数据'
})
局部过滤器
局部过滤器,定义在组件内部 filters 属性上.它只能在此组件内部使用.
过滤器的使用方式是,在双花括号或v-bind中通过一个管道符 ‘|’ 来拼接,
过滤器在vue3中被移除了 功能需要使用计算属性代替
路由原理
通过改变 URL,在不重新请求页面的情况下,更新页面视图。
路由分为History模式和Hash模式 我们经常使用的是History模式 前端的URL必须和实际向后端发起请求的URL一致 如果后端缺少对路由处理,将返回404错误
Hash在浏览器中符号的“#”,以及#后面的字符称之为hash hash虽然出现在URL中,但不会被包括在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。
监听hash值的改变,history模式监听路径的改变
给window绑定一个事件
路由传参:
三种:
分别是query,params,动态路由传参
接收:
通过query方式传递过来的参数一般是通过this.$route.query
接收
通过params方式传递过来的参数一般是通过this.$route.params
接收
通过动态路由传参方式传递过来的参数一般是通过this.$route.params
接收
Router-link路由传参方式URL后面传参,通过斜线进行传参
路由守卫:
2.路由守卫使用的方式有几种? 全局的 单个路由独享的 组件级的
3.vue-router全局有三个守卫:
router.beforeEach 全局前置守卫 进入路由之前
router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用 router.afterEach 全局后置钩子 进入路由之后 没有next
组件内的守卫:
进组组件前的守卫 beforeRouteEnter 没有办法获取this,当前组件没有创建
路由更新时的守卫 beforeRouteUpdata(2.2新增)
离开组件时的守卫 beforeRouteLeave
router-link
(不带参数) this.$router.push()
(函数里面调用)this.$router.replace()
(用法同push)this.$router.go(n)
go返回几级页面(正数是往前跳转页面,负数是往后跳转页面)在下次DOM更新循环结束后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。使用场景是:可以在created钩子函数中拿到dom节点
无法获取的原因:
vue的reader渲染函数是异步的,例如将变量修改,数据改变引起虚拟dom对比,如果立刻获取真实dom,无法获取,因为数据改变引起虚拟dom对比,立即获取真实dom是undefined,可以使用定时器、$nextTick
使用场景:
假如定义一个变量开关,修改开关变量让按钮和输入框来回切换,切换到input直接获取dom为其绑定自动聚焦,是不可以获取dom的,因为数据的变化引起了虚拟dom对比,立即获取真实dom是undefined,可以使用定时器、$nextTick
给ele-input绑定自定义指令v-focus,不起作用,因为ele-input被封装了,el-input绑定原生事件要加.native
如果为对象添加少量的新属性,可以直接采用Vue.set()
如果需要为新对象添加大量的新属性,则通过创建新对象的方式Object.assign()
如果你需要进行强制刷新时,可采取$forceUpdate() (不建议)
方案一:利用 this.set(this.obj,key,val) target:要修改的对象或数组
例:this.set(this.obj,‘k1’,‘v1’)
方案二:就利用 Object.assign({},this.obj)创建新对象 (额 晒恩)
如果是数组就 Object.assign([],this.obj)
如果是对象就 Object.assign({},this.obj)。
添加到对象的新属性不会触发更新
应创建一个新的对象,合并原对象和混入对象的属性
$forceUpdate如果你发现你自己需要在 Vue中做一次强制更新,99.9% 的情况,是你在某个地方做错了事
$forceUpdate迫使Vue 实例重新渲染
如果添加少量的数据使用方法一,添加大量的数据使用方法二
生命周期让我们在控制整个vue时更容易形成更好的逻辑,可以分为四个阶段,创建阶段,挂载阶段,更新阶段,销毁阶段
分别有:
创建前:beforeCreate() 只有一些实例本身的事件和生命周期函数
创建后:Created() 是最早使用data和methods中数据的钩子函数
挂载前:beforeMount() 指令已经解析完毕,内存中已经生成dom树
挂载后:Mounted() dom渲染完毕页面和内存的数据已经同步
更新前:beforeUptate() 当data的数据发生改变会执行这个钩子,内存中的数据是新的,页面是旧的
更新后:Updated() 内存和页面都是新的
销毁前:beforeDestroy() 即将销毁data和methods中的数据此时还是可以使用的,可以做一些释放内存的操作
销毁后:Destroyed() 已经销毁完毕
自定义组件分为全局自定义组件和局部自定义组件,全局组件可以全局使用,可以在所有的页面中调用引入
局部组件仅限当前引入页面
全局自定义组件
写好组件在main文件直接引入,这种是最简单的方式
import myTabItem from '@/components/myTabItem/myTabItem.vue'
Vue.component('my-tab-item', myTabItem) // 这种注册组件的方式叫做全局自定义组件html建议短横线分隔不建议驼峰
全局自定义组件是使用 Vue.component(‘组件名’, 组件模板)//组件模板就是引入组件路径定义的名字
局部自定义组件
在一个组件通过配置项 components注册的组件
常用指令
在vue中提供了一些对于页面 + 数据的更为方便的输出,这些操作就叫做指令,指令中封装了一些DOM行为, 结合属性作为一个暗号, 暗号有对应的值,根据不同的值,框架会进行相关DOM操作的绑定
vue中的指令有很多,我们平时做项目常用的有:
** v-if:是动态的向DOM树中添加或者删除元素;**
v-else是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用
** v-show:是通过标签的CSS样式display的值是不是none,控制显示隐藏**
** v-if和v-show区别:**
1、当条件为真的时候 没有区别 当条件为假的时候 v-if通过创建或删除DOM节点来实现元素的显示隐藏,v-show通过css中的display属性来控制
2、v-if更适合数据的筛选和初始渲染 v-show更适合元素的切换
v-for: v-for是根据遍历数据来进行渲染,要配合key使用。要注意的是当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中
v-on:用来绑定一个事件或者方法,简写方式是@click=“”
v-bind: v-bind用来动态的绑定一个或者多个属性。没有参数时,可以绑定到一个包含键值的对象。常用于动态绑定class和style。以及href等。简写的方式是“:属性名=“””一个冒号
v-model 只能适用于在表单元素上,可以实现数据双向绑定 ,
数据双向绑定实现的原理:
V2是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
v3 使用发布者订阅模式,结合ES6的prosy的setter和getter方法来进行数据劫持,进一步结合watchr的update方法
修饰符:
在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符常用的主要有:
• .stop :阻止事件冒泡
• .self :当事件作用在元素本身,触发回调
• .once :只执行一次
• .prevent: 阻止默认事件
• .capture :事件捕获
• ·passive:告诉浏览器你不想阻止事件的默认行为
• ·trim:自动过滤用户输入的首尾空格
语法:@事件名.修饰符=“方法名”0
生命周期有了一定的区别
Vue2--------------vue3
beforeCreate -> setup() 开始创建组件之前,创建的是data和method
created -> setup()
beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
mounted -> onMounted 组件挂载完成后执行的函数
beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
updated -> onUpdated 组件更新完成之后执行的函数。
beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
destroyed -> onUnmounted 组件卸载之前执行的函数。
activated -> onActivated 组件卸载完成后执行的函数
deactivated -> onDeactivated
vue2和vue3的一个最主要的区别:
在vue脚手架在4.5.15的时候,vue3还是会向下兼容vue2的命令,但是vue的脚手架一旦升级成为5.0.1以后vue3就会舍弃掉现有的命令行,不会再向下兼容vue2版本,但是在创建项目的时候5.0.1版本就会结合vite来创建项目,vite有一个优点就是创建项目的时候启动的非常快和vue的4.5.15版本不太一样,在vue的4.5.15它会在创建项目的时候把所有的依赖(vuex、vue-router等)全部安装完毕,但是5.0.1版本它是冷启动(什么是冷启动,就是什么依赖都不会去装)你需要哪个就去下载哪个,所以它启动的非常迅速
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性,性能优化 。
在不同环境,需要连接不同的资源:
比如dev环境,可能使用的数据库连接是某个远端的url
到了test环境,数据库连接可能存在一些变化——比如使用内网地址或库名有些变化……
这里希望达到的效果是,通过部署脚本来配置后端资源(数据库、缓存等)
这样各环境可以使用相同的构建物(docker image),从而保证代码一致性
这样在测试环境测试没问题的docker image,可以直接打标签部署到生产环境
毕竟没人能保证每次两次构建是完全一致的。
首先要安装axios,一般我会在项目的src目录中,新建一个utils文件夹,作为我们的网络请求模块,然后在里面新建一个http.js和一个api.js文件和一个reques.js。http.js文件用来封装我们的axios,在htt
p.js里面添加请求拦截器和响应拦截器api.js用来统一管理我们的接口url, reques.js用来调用封装我们需要使用到的方法
在request.js中添加请求拦截和响应拦截。在请求拦截中,会给请求头添加token字段,还有loading动画的开启。在响应拦截中,可以做一些loading动画的关闭,还有可以根据后端返回的状态码,做一些检验token是否有效或者过期的操作。接着就是做一些axios进行的api接口的封装,这里我用到了async,await封装请求接口函数,这样可以将异步操作同步化操作,代码更加友好,避免回调地域的出现。
一般在使用axios时,会用到拦截器的功能,一般分为两种:请求拦截器、响应拦截器。
请求拦截器
在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装;
响应拦截器
同理,响应拦截器也是如此功能,只是在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等。
插槽就是父组件往子组件中插入一些内容。
有三种方式,默认插槽,具名插槽,作用域插槽
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
v-for: v-for是根据遍历数据来进行渲染,要配合key使用。要注意的是当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中
Number、bollean、string、object、array、function、date、symbol Symbol函数
Props是单线程的传递
prop 数据单项传递,父影响子,子不影响父
不能在组件中直接修改 prop 传递过来的值,Vue 会给出警告
prop 验证时,会在实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的
非 prop 特性,组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。
如果不是一个函数,每个组件实例的data都是同一个引用数据,当该组件作为公共组件共享使用,一个地方的data更改,所有的data一起改变,如果data是一个函数,每个实例的data都在闭包中,就不会各自影响了。
data可以是对象也可以是函数,不会产生数据污染情况,函数有一个独立的作用域,函数内部的变量只能内部的函数去访问从而形成一个闭包
在mounted中加上this.$refs,或ref绑定一个对象
首先安装按需引入的插件,在main.js中添加按需引入的配置,之后在建好的js文件中首先导入vue,再导入需要的element-ui /vant-ui插件,通过vue.use()全局注入。
修改样式可以用样式穿透 /deep/
安装node-sass sass-loader npm install node-sass sass-loader --save
使用lang=”scss”
rem 是根 root em的缩写,1rem等于html根元素设定的font-size的px值
如果css里面没有设定html的font-size,则默认浏览器以1rem=16px来换算。
vw视窗的宽度 1vw=视窗宽度的1%
vh视窗高度,1vh=视窗高度的1%
(配置跨域、路径别名、打包分析、cdn映入、去掉console.log、配置第三方模块、ie兼容、eslint规范、图片压缩)
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是 computed 和 methods 的结合体;
值是包括选项的对象:选项包括有三个。
第一个handler:其值是一个回调函数。即监听到变化时应该执行的函数。
第二个是deep:其值是true或false;确认是否深入监听。
第三个是immediate:其值是true或false;确认是否以当前的初始值执行handler的函数
1 . computed 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;调用多次,只会执行一次
2 . methods 方法表示一个具体的操作,主要书写业务逻辑;
3 . watch 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是 computed 和 methods 的结合体;
Options API是散落式的存储
经常就是在data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变
Composition API是集中式存储
而Composition API就不一样了,它采用集中式进行存储的方法,和vue2不一样的是,他把所有的数据集中起来了,都集中在setup函数中
vue在项目打包之后,会生成一个dist文件夹。在dist文件夹里面又有一个js/app.js文件,这里主要存放的是整个项目的业务逻辑代码。随着项目不断的开发迭代,业务逻辑越来越多,app.js文件也会越来越大。在线上就会容易出现进入首页时所需时间过长或者出现白屏的问题。
使用路由懒加载可以分割代码,提高初始页的加载效率。
1、使用ES6的import ( ) --推荐使用**
const 组件名 = ( ) => import(‘组件路径’)
2、使用vue异步组件resolve
const 组件名 = resolve => require([‘组件路径’],resolve)
3、webpack提供的require.ensure( )
这种模式也可以通过参数中的webpackChunkName将js分开打包。
const 组件名 = resolve => require.ensure([], () => resolve(require(‘组件路径’)), ‘webpackChunkName’)
1.在我们HTML页面中,每一个元素都可以被看作一个盒子,而这个盒子由:内容区(content)、填充区(padding)、边框区(border)、外边界区(margin)四部分组成。
2.盒子模型有标准盒子模型和怪异盒子模型
标准模式下: 一个盒子的总宽度(页面中占的宽度)= width + margin(左右) + padding(左右) + border(左右)
怪异模式下: 一个盒子的总宽度= width + margin(左右)(即width已经包含了padding和border值)(IE浏览器)
box-sizing:content-box; 将采用标准模式的盒子模型标准
box-sizing:border-box; 将采用怪异模式的盒子模型标准
box-sizing:inherit; 规定应从父元素继承 box-sizing 属性的值。
1.为什么要清除浮动?
清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题
1.比如,我给父盒子设置一个boder,内部放两个盒子一个big、一个small,未给big和small设置浮动,则他们会默认撑开父盒子
2.如果给这两个盒子都加上了float属性的时候,底部的盒子就会顶上来,因为父盒子没设置高度,变成一条线,big和small已经浮动了
小结:当父元素不给高度的时候,内部元素不浮动的时候会撑开,而浮动时父元素会变成一条线,所以这个时候就需要解决浮动
2.清除浮动的四种方式
额外标签法(给最后一个浮动的标签后,新加一个标签,给其设置clear:both;,)(但这种方式是不推荐使用的,因为添加无意义的标签,语义化差)
父元素添加overfiow属性(触发BFC的方式,实现清除浮动)
使用after伪元素清除浮动
优点:符合闭合浮动思想,结构语义化正确,不容易出现其他为题
缺点:IE6-7不支持伪元素:after,使用zoom:1触发
使用before和after双伪元素清除浮动
除了这四种还有一种更为简单的方式,给父元素定义好height高度,就解决了父级元素无法获取到高度的问题。
BFC就是“块级格式化上下文”的意思,简单来说,BFC 就是一种属性,这种属性会影响着元素的定位以及与其兄弟元素之间的相互作用。BFC是一块用来独立的布局环境,保护其中内部元素不受外部影响,也不影响外部。
1.BFC触发的几种方式:
2.BFC的应用
方法一:给父元素设置成弹性盒子,子元素横向居中,纵向居中
方法二:父相子绝后,子部分向上移动本身宽度和高度的一半,也可以用transfrom:translate(-50%,-50%)(最常用方法)
方法三:父相子绝,子元素所有定位为0,margin设置auto自适应
两栏布局,左边定宽,右边自适应
左边左浮动,右边加oveflow:hidden;变成BFC清除左侧浮动元素的影响
三栏布局,圣杯布局、双飞翼布局
圣杯布局是利用父容器的左、右内边距加两个从列相对定位;
双飞翼布局把主列嵌套在一个新的父级块中利用左、右外边距进行布局调整
原来我做的主要是后台管理和一些移动端的项目,这两种布局呢是我原来查阅资料时看到的,所以就做了个简单的理解。
圣杯布局和双飞翼布局是我们需要日常掌握的重要布局方式。两者的功能相同,都是为了实现一个两侧宽度固定,中间宽度自适应的三栏布局。(中间先加载渲染)
首先要给两侧设置padding预留出相应的空间
随后分别为三列设置宽度与浮动,同时对footer设置清除浮动
根据浮动的特性,由于center的宽度为100%,即占据了第一行的所有空间,所以left和right被“挤”到了第二行。
接下来的工作是将left放置到之前预留出的位置上,这里使用负外边距
这里使用position: relative和right: 200px将left的位置在原有位置基础上左移200px,以完成left的放置
接下来放置right,只需添加一条声明即可
至此,布局效果完成。不过还需要考虑最后一步,那就是页面的最小宽度:要想保证该布局效果正常显示,由于两侧都具有固定的宽度,所以需要给定页面一个最小的宽度,但这并不只是简单的200+150=350px。回想之前left使用了position: relative,所以就意味着在center开始的区域,还存在着一个left的宽度。所以页面的最小宽度应该设置为200+150+200=550px
Flex布局是css3中新增的一种布局方式,他大多用于移动端中,在PC端中用的比较少。
在PC端中呢会存在一些兼容性的问题,因为flex是css3中新增的一些布局方式,旧版本的浏览可能不支持,所以有时候需要添加浏览器兼容的前缀来解决。
Flex 布局,可以简便、完整、响应式地实现各种页面布局,任何一个容器都可以指定为 Flex 布局,行内元素也可以使用 Flex 布局。
注意,设为 Flex 布局以后,子元素的float、clear和vertical-align属性将失效。
我在项目中常用到的有九宫格布局,列表布局等,都会经常用到。
Flex的属性:
在CSS中规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,比如div默认display属性值为“block”,成为“块级”元素;span默认display属性值为“inline”,是“行内”元素。
我们在平常的项目中经常使用到的有
• 行内元素有:span a b i img input select strong
• 块级元素有:div p h1-h6 ul table form ul ol li dl dt dd…
• 空元素(没有内容):
CSS hack是通过在CSS样式中加入一些特殊的符号,让不同的浏览器识别不同的符号(什么样的浏览器识别什么样的符号是有标准的,CSS hack就是让你记住这个标准),以达到应用不同的CSS样式的目的。
一般来说是针对不同的浏览器写不同的CSS,就是 CSS Hack。
CSS Hack常见的有三种形式:属性Hack、选择符Hack、条件注释Hack, Hack主要针对IE浏览器
1、 条件Hack
条件注释只有在IE浏览器下才能执行,这个代码在非IE浏览下被当做注释视而不见。可以通过IE条件注释载入不同的CSS、JS、HTML和服务器代码等。
2、 选择符 Hack
比如IE6能识别 *html .class{},
IE7能识别*+html .class{}
3、 属性 Hack
比如IE6能识别下划线“_”和星号“*”,IE7能识别星号“*”,但不能识别下划线”_ ”,而firefox两个都不能认识。
写CSS hack需要遵循以下三条原则:
• 有效: 能够通过 Web 标准的验证
• 只针对太古老的/不再开发的/已被抛弃的浏览器, 而不是目前的主流浏览器
• 代码要丑陋。让人记住这是一个不得已而为之的 Hack, 时刻记住要想办法去掉它。现在很多hacks已经抛弃了最初的原则,而滥用hack会导致浏览器更新之后产生更多的兼容性问题。因此,并不推荐使用CSS hack来解决兼容性问题。
九、src与href的区别
href 是指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,主要用于超链接。
经常用到的主要是css外部样式的链接,a标签超链接的使用
src是指向外部资源的位置,指向的内容将会下载并应用到文档内;
经常在script引入时,还有img图片的请求时用到
Frame:是一个现在废弃的标签,可以使一个网站显示在当前的页面上
页面中使用CSS的方式主要有3种:行内添加style属性,页面头部内嵌和外部链接,其中外部引用有两种:Link引入和@import导入
link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
link支持使用Javascript控制DOM去改变样式;而@import不支持。
css3中新增的一些特性大概可以分为:选择器,背景和边框属性,文本效果,2D/3D转换
4.2D/3D转换
变形transform:
有rotate旋转、scale缩放、translate位移、skew倾斜
过渡transition:
transition是一个复合属性,可以同时定义transition-property、transition-duration、transition-timing-function、transition-delay子属性值
动画animation
动画的使用,首先通过@(-webkit-)keyframes 定义动画名称及动画的行为,再通过animation属性设置动画特征相关值进行调用
这些新属性大多在新版浏览器得到了支持,有些需要添加浏览器前缀(-webkit-、-moz-、-ms-、-o-)
在不同的浏览器中,浏览器的内核都是不相同的,所以各个浏览器对网页的解析存在一定的差异。
浏览器内核主要分为两种,一是渲染引擎,另一个是js引擎,所以浏览器的兼容性问题一般是指:css兼容、js兼容
1.不同浏览器的标签默认的margin和padding不同
解决办法:可以通过设置全局样式来解决这个问题,这样所有的样式都会重置,初始值都会相同了。
2.css3新属性,加浏览器前缀兼容早期浏览
-moz- / 火狐浏览器 /
-webkit- / Safari, 谷歌浏览器等使用Webkit引擎的浏览器 /
-o- / Opera浏览器(早期) /
-ms- / IE /
3.块属性标签float后,又有横行的margin的情况下,IE浏览器margin加倍的问题
设置为float的div在IE下设置的margin会加倍。这是一个IE6都存在的bug。解决方案是在这个div里面加上display:inlin-block.变为行内块元素
4.设置较小高度标签(小于一般10px),在IE6,IE7,遨游中高度超出自己设置高度
可以通过给高出的标签设置overflow:hidden
或设置行高line-height小于你设置的高度
5.行内属性标签,设置display:black后采用float布局,又有横行的margin的情况下,IE6间距bug
IE6里的间距比超过设置的间距,可以通过在display:block;后面加入display:inline;display:table;
6.IE浏览器div最小高度和宽度的问题
加宽高,并且宽高加auto
7.超链接访问过hover样式就不出现的问题
被点击访问过的超链接样式不在具有hover和active了,很多人应该都遇到过这个问题,解决技巧是改变CSS属性的排列顺序: L-V-H-A
a:link{}>a:visited{}>a:hover{}>a:active{}
8.图片默认有间距
几个img标签放在一起的时候,有些浏览器会有默认的间距,通配符清除间距也不起作用。
可以通过使用float属性为img布局(所有图片左浮)
9.css hack解决浏览器兼容性
不同浏览器,识别不同的样式,css hack本身就是处理浏览器兼容的
1.事件绑定
IE: dom.attachEvent();
标准浏览器: dom.addEventListener(‘click’,function(event){},false);
var x = document.getElementById("myBtn");
if (x.addEventListener) { //所有主流浏览器,ie9+
x.addEventListener("click", myFunction);
} else if (x.attachEvent) { // IE 8 及更早 IE 版本
x.attachEvent("onclick", myFunction);
}
2.event事件对象问题
document.onclick=function(ev){//谷歌火狐的写法,IE9以上支持,往下不支持;
var e=ev;
console.log(e);
}
document.onclick=function(){//谷歌和IE支持,火狐不支持;
var e=event;
console.log(e);
}
document.onclick=function(ev){//兼容写法;
var e=ev||window.event;
var mouseX=e.clientX;//鼠标X轴的坐标
var mouseY=e.clientY;//鼠标Y轴的坐标
}
3.event.srcElement(事件源对象)问题
IE: event对象有srcElement属性,但是没有target属性;
Firefox: event对象有target属性,但是没有srcElement属性。
解决办法:srcObj = event.srcElement?event.srcElement:event.target;
4.获取元素的非行间样式值
IE: dom.currentStyle[‘width’] 获取元素高度
标准浏览器:window.getComputedStyle(obj, null)[‘width’];
// 获取元素属性值的兼容写法
function getStyle(obj,attr){
if(obj.currentStyle){
//兼容IE
obj.currentStyle[attr];
return obj.currentStyle[attr];
}else{
//非IE,
return window.getComputedStyle(obj, null)[attr];
}
}
5.阻止事件冒泡传播
//js阻止事件传播,这里使用click事件为例
document.onclick=function(e){
var e=e||window.event;
if (e.stopPropagation) {
e.stopPropagation();//W3C标准
}else{
e.cancelBubble=true;//IE.... true: 不被上层元素的事件控制
}
}
6.阻止事件默认行为
//js阻止默认事件 一般阻止a链接href,form表单submit提交
document.onclick=function(e){
var e=e||window.event;
if (e.preventDefault) {
e.preventDefault();//W3C标准
}else{
e.returnValue='false';//IE.. false: 不会进行判断就直接执行下去
}
}
<script>
window.onload = function(){
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
//1.创建ajax对象
//只支持非IE6浏览器
var oAjax = null;
if(window.XMLHttpRequest){
oAjax = new XMLHttpRequest();
//alert(new XMLHttpRequest());
}else{
//只支持IE6浏览器
oAjax = new ActiveXObject("Microsoft.XMLHTTP");
}
//2.连接服务器,这里加个时间参数,每次访问地址都不一样,浏览器就不用浏览器里的缓冲了,但
// 但服务器那端是不解析这个时间的
oAjax.open("get","a.txt?t=" + new Date().getTime(),true);
//3.发送
oAjax.send(null);
//4.接受信息
oAjax.onreadystatechange = function(){
//浏览器与服务器之间的交互,进行到哪一步了,当等于4的时候,代表读取完成了
if(oAjax.readyState==4){
//状态码,只有等于200,代表接受完成,并且成功了
if(oAjax.status==200){
alert("成功" + oAjax.responseText);
}else{
alert("失败");
}
}
};
};
};
script>
JavaScript分三个部分:
1. ECMAScript标准—基本语法
2. DOM—>Document Object Model 文档对象模型,操作页面元素的
3. BOM—>Browser Object Model 浏览器对象模型,操作浏览器的
浏览器中有个顶级对象:window----皇上
页面中顶级对象:document-----总管太监
页面中所有的内容都是属于浏览器的,页面中的内容也都是window的,因为页面中的所有内容都是window的,window是可以省略的.
变量是window的
什么是原型?
在js中,每当定义一个函数数据类型(Object、Function、Arrry、Date等)的时候都会自带一个prototype对象,这个对象就是我们说的原型。原型又分为显示原型和隐式原型
显示原型是函数里面的prototype属性,每个prototype原型都有一个constructor属性,指向它关联的构造函数。
隐式原型是实例化对象里面的__proto__属性,__proto__属性指向自身构造函数的显示原型prototype属性
什么是原型链
每一个实例化对象都有一个__proto__属性,而这个__proto__属性指向构造函数的原型对象,
原型对象上也有一个__proto__属性,就这样一层一层往上找,直到找到object.phototype,就这样查找的过程就叫原型链
proto prototype constructor的三角关系
函数在声明时会生成一个对象prototype 该对象中有一个constructor指向构造函数本身 当构造函数实例化后,
在实例化对象中会生成一个对象叫__proto__指向构造函数的prototype
1.作用域:
作用域就是一个变量可以使用的范围,主要分为全局作用域和函数作用域
全局作用域
在最外层定义的变量或者方法,全局都可以使用,所以是全局作用域。
函数作用域(局部作用域)
js中可以通过函数来创建一个独立作用域称为函数作用域,函数可以嵌套,所以作用域也可以嵌套
ES6的定义的块级作用域
声明变量的方法 有var let const
var 声明的变量可以全局使用,存在变量提升,可以重新赋值
let 声明的变量只能在当前作用域内使用
const 声明的是常量,只能在当前作用域中使用
let/const声明的变量让当前代码块变成一个暂时性的死区
他们声明的变量不存在变量提升,在同一作用域内不能重新赋值
区别: const声明的变量必须给默认值 const声明的是常量不能重新赋值
const声明的变量的值如果是引用数据类型 则数据内部的数据可以修改
变量提升
var声明的变量,function声明的函数存在变量提升
let const 不会变量提升
函数变量的优先级比变量的优先级大
2.自由变量:
假如在全局中定义了变量a,在函数中使用了这个a,这个a就是自由变量,可以这样理解,凡是跨了自己的作用域的变量都叫自由变量。
3.作用域链
自由变量的向上级作用域一层一层查找,直到找到为止,最高找到全局作用域,就形成了作用域链。
闭包:
简单的理解就是函数中套了一个函数,内层函数可以访问外层函数中的变量
闭包就是能够读取其他函数内部变量的函数 还可以作为回调函数,可以实现函数的复用
优点:1.能够读取函数内部的变量 2.让这些变量一直存在于内存中,不会在调用结束后,被垃圾回收机制回收
缺点:闭包会使函数中的变量保存在内存中,内存消耗很大
垃圾回收:
垃圾回收,顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
在js中this不是固定不变的,它会随着执行环境的改变而改变。
this的调用大概分为五种场景:
1.浏览器里,在全局范围内的this 指向window对象;
2.在函数中,this永远指向最后调用他的那个对象;
3.构造函数中,this指向new出来的那个新的对象;
4.箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.,箭头函数的this指向是静态的,声明的时候就确定了下来;
改变this指向
call:参数是单个使用的,
apply:参数是一个集合时使用,
bind:使用bind会改变this,不会改变数据,需要在调用的地方加一个括号
垃圾回收:
垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
内存泄漏:
js中的一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入eventqueue,然后在执行微任务。
宏任务和微任务都包含一些事件
宏任务:setTimeout,setInterval,Ajax,DOM事件
微任务:Promise async/await
要想明白这个机制 就要理解js单线程。因为JS是单线程语言,只能同时做一件事儿。js任务需要排队顺序执行,如果一个任务时间过长,后边的任务也会等着。假如,我们在请求一个网址时,图片加载很慢,网页总不能一直卡不出来,这个时候就可以用异步来解决了
异步就是由单线程这个背景而来的,解决了单线程等待的这个问题,异步的特点不会阻塞后面代码的执行。也就是请求处理过程中,你不能闲着,会产生异步的请求,回头再处理,然后继续执行下面的请求
异步和单线程是相辅相成的,js是一门单线程脚本语言,所以需要异步来辅助
基本数据类型(值类型): Number、String、Boolean、Undefined、Null、Symbol(es6新增独一无二的值) 和 BigInt(es10新增);
引用数据类型: Object。包含Object、Array、 function、Date、RegExp。
备注: 基本数据类型,又称值类型。
1.基本数据类型和引用数据类型的区别:
基础数据类型,都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问 。
引用数据类型,引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。因此,引用类型的值都是按引用访问的。
2.Typeof运算符:
typeof 能有效检测基本类型,检测引用类型都返回object,其中null属于特殊的引用类型返回object,function属于特殊引用类型不用于存储数据,typeof检测返回function.
3.==和===
===三等表示全等,判断左右两边对象或值是否类型相同且值相等。
==二等表示值相等。判断操作符两边对象或值是否相等类型可以不同,类型不同时,使用Number()转换成Number类型在进行判断。例外规则,null==undefined,null/undefined进行运算时不进行隐式类型转换。通常把值转为Boolean值,进行条件判断。Boolean(null)===Boolean(undefined)>false===false 结果为true
4.if语句和逻辑运算
所有基本类型中Boolean值是false的只有6个,分别是 : 0 NaN ’ ’ null undefined false
引用类型Boolean值全是true.
if条件是单个值时,如果是truly值,条件成立, 如果是falsely值,条件不成立
深拷贝和浅拷贝的区别
1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用
2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
arr=[100, [{a : 'hello'}, {b : "world"}], { c: "123456789" }];
//判断修改的是不是'object'或者null,如果不是 object 或者 null 那么直接返回
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
let result;
//判断上面下来的obj是不是数组 用instanceof来检测 因为是数组应用类型
obj instanceof Array?result=[]:result={}
for (var item in obj) {
//查找一个对象是否有某个属性
if (obj.hasOwnProperty(item)) {
// 递归调用
result[item] = deepClone(obj[item])
}
}
return result;
}
let arr1=deepClone(arr);
arr1[1][0].a='vue'
arr1[0]=99
console.log('原数组',arr)
console.log('新数组',arr1)
数据类型判断大概有四种typeof、instanceof、constructor、Object.prototype.toString.call()
1.Typeof:
不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
基本数据类型中:Number,String,Boolean,undefined 以及引用数据类型中Function ,可以使用typeof检测数据类型,分别返回对应的数据类型小写字符。
另:用typeof检测构造函数创建的Number,String,Boolean都返回object
基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object
2 . instanceof
它用来判断这个构造函数的原型是否在给定对象的原型链上。
3.constructor
Constructor就是判断是否是一个实例对象,若实例对象上没有实例属性或方法时,就去原型链上寻找
jquery.type()
如果对象是undefined或null,则返回相应的“undefined”或“null”。 其他一切都将返回它的类型
4 . 使用Object.prototype.toString.call()检测对象类型
可以通过toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为thisArg。
1.push() 添加到数组尾部
2.pop() 从数组尾部删除一个元素
3.unshift()添加到数组的头部
4.shift() 从数组头部删除一个元素
5.slice() 从数组中截取
7.reverse() 数组翻转(瑞沃肆)
8.sort() 数组排序
9.join() 数组拼接
10. filter () 用于过滤数组成员,满足条件的成员组成一个新数组返回;
11.toString() 数组转字符串
indexOf() 从前往后遍历,返回item在数组中的索引位,如果没有返回-1;
lastIndexOf() 与indexOf一样,区别是从后往前找。
Array.from() 用于类似数组的对象(即有length属性的对象)和可遍历对象转为真正的数组
every()该方法对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回true。
map() 将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回
forEach() forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数。
1、冒泡排序法
从序列的最右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置,重复这一操作,实现所有数字从小到大或从大到小排列的算法即冒泡排序。
2、选择排序
从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换,重复这一操作的算法即选择排序。
3、插入排序
从序列左端开始依次对数据进行排序的算法称为插入排序。(序列中数据分为已排序区和未排序区,依次对每个数据和已排序区比较,进行排序)
4、 快速排序
快速排序的原理,简单来说就是把一个事情,分成很多小事情来处理,分治的思想。如找到一个基准,将大于它的放到右边,小于它的所有数放到左边,再分别对左右的数组重复此步骤,最后数组将按照从小到大的顺序排列。
5、 sort排序法
sort是一个方法,该方法内部对该数组对象进行一些操作,又返回来一个数组,不传参数的话排序默认根据字符串的Unicode排序。
数组扁平化就是将一个多维数组转换为一个一维数组
实现基本方式
1、对数组的每一项进行遍历。
2、判断该项是否是数组。
3、如果该项不是数组则将其直接放进新数组。
4、是数组则回到1,继续迭代。
5、当数组遍历完成,返回这个新数组。
八、字符串
①从遍历数组角度来说,for···in遍历出来的是key(即下标),for···of遍历出来的是value(即数组的值);
②从遍历字符串的角度来说,同数组一样。
③从遍历对象的角度来说,for···in会遍历出来的为对象的key,但for···of会直接报错。
④如果要使用for…of遍历普通对象,需要配合Object.keys()一起使用。
在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等
1.null表示没有对象,可能将来要赋值一个对象,即该处不应该有值
1) 作为函数的参数,表示该函数的参数不是对象
2) 作为对象原型链的终点
1.浏览器里,在全局范围内的this 指向window对象;
2.在函数中,this永远指向最后调用他的那个对象;
3.构造函数中,this指向new出来的那个新的对象;
4.Call、apply、bind中的this被强绑定在指定的那个对象上;
Call、apply可以修改this指向 他的第一个参数就是要修改的函数内部的this指向 指向的对象
call可以以散落的形式给调用call方法的函数传递参数
apply可以以数组的形式给调用apply方法的函数传递参数
bind 的参数是要修改的this指向 指向的对象 但是bind的返回值是调用bind方法的函数本身 函数需要再次调用才会执行
5.箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来;
6.apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参。
1.原型链继承
父类的实例作为子类的原型
优点:简单易于实现,父类的新增实例与属性子类都能访问
缺点:
1).可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
2)无法实现多继承
3)创建子类实例时,不能向父类构造函数中传参数
2.借用构造函数继承(伪造对象、经典继承)
复制父类的实例属性给子类
优点:
1)解决了子类构造函数向父类构造函数中传递参数
2)可以实现多继承(call或者apply多个父类)
缺点:
1)方法都在构造函数中定义,无法复用
2)不能继承原型属性/方法,只能继承父类的实例属性和方法
3.实例继承(原型式继承)
优点:
1)不限制调用方式
2)简单,易实现
缺点:
不能多次继承
4.组合式继承
调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
缺点:
由于调用了两次父类,所以产生了两份实例
优点:
1)函数可以复用
2)不存在引用属性问题
3)可以继承属性和方法,并且可以继承原型的属性和方法
ES5继承和ES6继承的区别:
es5继承首先是在子类中创建自己的this指向,最后将方法添加到this中
Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)
es6继承是使用关键字先创建父类的实例对象this,最后在子类class中修改this
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
DOM是网页中用来表示文档中对象的标准模型,他是由节点和对象组成的结构集合。在浏览器解析HTML标签时,会构建一个DOM树结构,把html结构化成js可以识别的树模型。
树模型构成的层级结构,可以很容易的表明家族成员之间的关系,把复杂的关系简明地表示出来
由此呢js也提供了一些dom的操作
浏览器对象模型(BOM :Browser Object Model)是JavaScript的组成之一,它提供了独立于内容与浏览器窗口进行交互的对象,使用浏览器对象模型可以实现与HTML的交互。它的作用是将相关的元素组织包装起来,提供给程序设计人员使用,从而降低开发人员的劳动量,提高设计Web页面的能力。
BOM是一个分层结构
浏览器中有个顶级对象:window----皇上
页面中顶级对象:document-----总管太监
页面中所有的内容都是属于浏览器的,页面中的内容也都是window的,因为页面中的所有内容都是window的,window是可以省略的.
变量是window的
DOM0级事件
DOM0 级时间分两种,一是直接在标签内直接添加执行语句,二是定义执行函数。
DOM2 级事件
第一个参数:事件名称
第二个参数:执行函数
第三个参数:指定冒泡还是捕获,默认是false,冒泡。
element.addEventListener('click',function(){},false)
DOM3 级事件
同DOM2级一样,只不过添加了更多的事件类型,鼠标事件、键盘事件
element.addEventListener('keyup',function(){},false)
事件类型分两种:事件捕获、事件冒泡。
事件捕获就是由外往内,从事件发生的顶点开始,逐级往下查找,一直到目标元素。
事件冒泡就是由内往外,从具体的目标节点元素触发,逐级向上传递,直到根节点。
事件流就是,事件传播过程。
DOM完整的事件流包括三个阶段:事件捕获阶段、目标阶段和事件冒泡阶段。
事件通过捕获到达目标元素,这个时候就是目标阶段。从目标节点元素将事件上传到根节点的过程就是第三个阶段,冒泡阶段。
当事件发生在 DOM元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,之后是document对象,一直到触发事件的元素。
当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,直到到达window为止。
var eve = new Event('test'); //通过new Event 创建事件
dom.addEventListener('test', function () { //注册事件
console.log('test dispatch');});
setTimeout(function () {
dom.dispatchEvent(eve); //触发事件
}, 1000);
瀑布流:无限上拉列表中,如果给每一个图片绑定点击事件,非常繁琐且消耗内存。所以我们可以把每张图片上的点击事件委托给共同的父元素。
事件委托,又名事件代理。事件委托就是利用事件冒泡,就是把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托也就没法实现了
好处:提高性能,减少了事件绑定,从而减少内存占用
应用场景 在vue中事件委托
我们经常遇到vue中v-for一个列表,列表的每一项都绑定了@click处理事件。我们都知道绑定这么多监听,从性能方面来说是不太好的。那我们我们可以通过把每个item的click事件委托给父元素的形式来实现
需要点击每个a,来。弹出他们的内容
<div id="div3">
<a href="#">a1a><br>
<a href="#">a2a><br>
<a href="#">a3a><br>
<a href="#">a4a><br>
<button id='btn1'>加载更多...button>
div>
<script>
// 封装通用的事件绑定函数
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
//获取父元素
const fu = document.getElementById('div3')
bindEvent(fu, 'click', function (event) {
// console.log(event.target) // 获取触发的元素
let target=event.target
event.preventDefault() // 阻止默认行为
//过滤符合条件的子元素,主要是过滤掉 加载更多
if(target.nodeName.toLowerCase()==="A"){
alert(target.innerHTML;
}
})
script>
1.创建xhr 核心对象
var xhr=new XMLHttpRequest();
2.调用open 准备发送
参数一:请求方式
参数二: 请求地址
参数三:true异步,false 同步
xhr.open(‘post’,‘http://www.baidu.com/api/search’,true)
3.如果是post请求,必须设置请求头。
xhr.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’)
4.调用send 发送请求 (如果不需要参数,就写null)
xhr.send(‘user=tom&age=10&sex=女’)
5.监听异步回调 onreadystatechange
判断readyState 为4 表示请求完成
判断status 状态码 为 200 表示接口请求成功
responeseText 为相应数据。字符串类型。
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
console.log(xhr.responseText);
var res=JSON.parse(xhr.responseText);
console.log(res);
if(res.code==1){
modal.modal('hide');
location.reload();
}
}
}
备注:如果是post请求,想要传json格式数据。
设置请求头
1.xhr.setRequestHeader('Content-Type', 'application/json')
open发送数据
2.xhr.open({_id:xxx,user:xxxx,age:xxxx})
JSONP原理:
ajax 请求受同源策略影响,不允许进行请求,我们利用 script 标签的 src 属性不受同源策略的约束,利用这个特性jsonp需要以下步骤:
1.动态创建(document.createElement('script'))
2.设置src 属性,(src中要包含参数callback=fn)进行跨域请求
3.将 添加到页面中执行 (body.appendChild('script'))
4.页面要提前定义好callback。
5.后端会返回回调函数执行并包裹参数callback(data)
备注:
服务端不再返回JSON格式的数据,而是返回回调函数包裹数据(fn({name:‘tom’,age:18}),在src中进行了调用,这样实现了跨域。
本地存储分为cookie、localStorage、sessionStorage,
Cookie
Cookie设计初衷是用来和服务器通讯,而不是本地存储,他只是被‘借用’到本地存储。
优点:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
缺点:存储空间大小只有4KB左右 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
localStorage、sessionStorage主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题
localStorage、sessionStorage优点:
localStorage和sessionStorage:可以保存5MB的信息。
localStorage和sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信
localStorage、sessionStorage缺点:
localStorage:除非被手动清除,否则将会永久保存。
sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。
localStorage可以用来跨页面传递参数,sessionStorage用来保存一些临时的数据,防止用户刷新页面之后丢失了参数。他们两个都是H5才应用的新特性
API有哪些
setItem(存储) getItem(取出) clear(删除所有) removeItem(删除某一个) stringify()用于从一个对象解析出字符串;parse()用于从一个字符串中解析出json对象。
注意:localStorage只支持string类型的存储。
使用关键字new创建新实例对象经过了以下几步:
在ES5中,声明变量只有var和function两种形式。但是因为var声明的变量会有一定的缺点(内层变量可能覆盖外层变量的问题以及用来计数的循环变量泄露为全局变量,下面有介绍),ES6提出了使用let和const声明变量,弥补了ES5中var的缺点。
1.是否存在变量提升?
var声明的变量存在变量提升(将变量提升到当前作用域的顶部)。即变量可以在声明之前调用,值为undefined。
let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。
2.是否存在暂时性死区?
let和const存在暂时性死区。即只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
3.是否允许重复声明变量?
var允许重复声明变量。
let和const在同一作用域不允许重复声明变量。
解构赋值就是从目标对象或数组中提取自己想要的变量。最常用的场景是:element-ui,vant-ui按需引入,请求接口返回数据,提取想要数据。
常见的几种方式有
1.默认值
2.交换变量
3.将剩余数组赋给一个变量
当结构一个数组时,可以使用剩余模式,将数组剩余部分赋值给一个变量
4.给新的变量名赋值
可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名
1、箭头函数是匿名函数,不能作为构造函数,不能使用new
箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式:一种只包含一个表达式,连{ … }和return都省略掉。还有一种可以包含多条语句,这时候就不能省略{ … }和return。
2.箭头函数内没有arguments,可以用展开运算符…解决
arguments:是一个方法调用的集合,是一个伪数组,不是真的数组,不具有数组的操作的方法,可以用展开运算解决(…)
3.箭头函数的this,始终指向父级上下文(箭头函数的this取决于定义位置父级的上下文,跟使用位置没关系,普通函数this指向调用的那个对象)
4.箭头函数不能通过call()、apply()、bind()方法直接修改它的this指向。
5.箭头函数没有原型属性
传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。这样的写法相对于其它传统面向对象语言来讲,独树一帜也可以说难以接受!
ES5中如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。
构造函数生成实例的执行过程:
1.当使用了构造函数,并且new 构造函数(),后台会隐式执行new Object()创建对象;
2.将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象。
3.执行构造函数的代码。
4.返回新对象(后台直接返回);
ES6中的类
ES6引入了class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得javascript在对象写法上更加清晰,更像是一种面向对象的语言。
注意项:
1.在类中声明方法的时候,千万不要给该方法加上function关键字
2.方法之间不要用逗号分隔,否则会报错
ES5中的继承 (组合继承:原型链继承 + 借用构造函数)
原型链继承:
父类的实例作为子类的原型
借用构造函数继承:
在子类内,使用call()调用父类方法,并将父类的this修改为子类的this.相当于是把父类的实例属性复制了一份放到子类的函数内.
组合继承:
既能调用父类实例属性,又能调用父类原型属性
什么是promise
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上说,promise 是一个对象,从它可以获取异步操作的的最终状态(成功或失败)。
Promise是一个构造函数,对外提供统一的 API。
名词约定
promise(首字母小写)指的是“Promise实例对象”
Promise 首字母大写且单数形式,表示“Promise构造函数”
Promises 首字母大写且复数形式,用于指代“Promises规范”
Promise对象的状态不受外界影响
1)pending 初始状态
2)fulfilled (否file得)成功状态
3)rejected (瑞杰克特得)失败状态
Promise 有以上三种状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态
Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 pending变成fulfilled或者由pending变成rejected
使用 new 来创建一个promise对象。
Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是就是「回调函数」
Resolve(瑞色福)函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
then()方法: then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
catch()方法: 当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。
all()方法: Promise 的 all 方法提供了并执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
race()方法: race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。
async
async 是“异步”的简写, async 用于申明一个异步的 function
await
await 可以认为是 async wait 的简写,await 用于等待一个异步方法执行完成。
特点:
asayc的用法,它作为一个关键字放到函数前面,这样普通函数就变为了异步函数
异步async函数调用,跟普通函数的使用方式一样
异步async函数返回一个promise对象
async函数配合await关键字使用(阻塞代码往下执行)是异步方法,但是阻塞式的
使用场景:async主要来处理异步的操作,
需求:执行第一步,将执行第一步的结果返回给第二步使用。在ajax中先拿到一个接口的返回数据后使用第一部返回的数据执行第二步操作的接口调用,达到异步操作。
async/await是建立在 Promises上的,不能被使用在普通回调以及节点回调写法更加优雅,
promise是成熟的方案,async/await是es7的最终解决异步方案 同步的写法,书写异步的操作
async await就是generator语法糖,所谓语法糖按照我自己理解它就是一种方式,你使用别的方法也可以达到目的,使用语法糖会更方便更快,例如你走路可以到北京,坐飞机也可以到北京,可以用generator来模拟async await。
他已经被async函数取代掉了,函数在前面加了一个就变成了generator函数
generator函数跟普通函数在写法上的区别就是,多了一个星号,并且只有在generator函数中才能使用yield,什么是yield呢,他相当于generator函数执行的中途暂停点。而怎么才能暂停后继续走呢?那就得使用到next方法,next方法执行后会返回一个对象,对象中有value 和 done两个属性
value:暂停点后面接的值,也就是yield后面接的值
done:是否generator函数已走完,没走完为false,走完为true
Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
100-199 提示信息 – 表示请求正在处理
200-299 成功 – 表示请求正常处理完毕
300-399 重定向 – 要完成请求必须进行更进一步的处理
400-499 客户端错误 – 请求有语法错误或请求无法实现
500-599 服务器端错误 – 服务器处理请求出错
200:请求成功,浏览器会把响应体内容(通常是html)显示在浏览器中;
404:(客户端问题)请求的资源没有找到
400: 语义有误,当前请求无法被服务器理解。
401: 当前请求需要用户验证
403: 服务器已经理解请求,但是拒绝执行它。
500:(服务端问题)请求资源找到了,但服务器内部发生了不可预期的错误;
301/302/303:(网站搬家了,跳转)重定向
大致过程是:
浏览器的地址栏输入URL并按下回车,
查找当前的URL是否存在缓存,并比较缓存是否过期,
DNS解析URL对应的IP,
根据IP建立TCP连接(三次握手),
HTTP发起请求,服务器处理请求,浏览器接收HTTP响应,
渲染页面,构建DOM树,
关闭TCP连接(四次挥手)。
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
简单理解下就是协议、域名、端口都相同才同域,否则都是跨域
理解跨域的概念:协议、域名、端口都相同才同域,否则都是跨域
跨域就是指浏览器不允许当前页面的所在源,去请求另一个源的数据
1.分支明细
(1)主分支(master):第一次向 git 仓库中提交更新记录时自动产生的一个分支。
(2)开发分支(develop):作为开发的分支,基于 master 分支创建。
(3)功能分支(feature):作为开发具体功能的分支,基于开发分支创建
2.分支命令
(1)git branch 查看分支
(2)git branch 分支名称 创建分支
(3)git checkout 分支名称 切换分支
(4)git merge 来源分支 合并分支 (备注:必须在master分支上才能合并develop分支)
(5)git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)
3.暂时保存更改
(1)存储临时改动:git stash
(2)恢复改动:git stash pop
多人冲突:
是当前修改是左箭头方向,传入的是右箭头的方向,中间用等于号分割,等号上边是当前修改,下边是传入的修改。
两人同时提交可能会出现冲突,解决办法是手动修改冲突
HTTP与HTTPS的区别
1、HTTP是超文本传输协议,信息是明文传输,HTTPS是具有安全性的SSL加密传输协议。
2、HTTPS协议需要ca申请证书,一般免费证书少,因而需要一定费用。
3、HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样。前者是80,后者是443。
4、HTTP连接是无状态的,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,安全性高于HTTP协议。
三次握手:
第一次:建立连接时,客户端发送syn(syn=j)包到服务器,等待服务端确认
第二次:服务器收到syn包,必须确认客户的syn(ack=j+1),同时也发送一个syn(syn=k)包,即syn+ACK包
第三次:客户端收到服务器的syn和ack包,向服务器发送确认包ack(ack=k+1),发送完毕,客户端和服务端连接成功,完成三次握手
四次挥手:
第一次:浏览器发送完数据后,发送fin请求断开连接
第二次:服务器发送ack到客户端,确认客户端的断开请求
第三次:服务器请求断开fin的请求
第四次:客户端确认服务器的断开ack
使用基于 Token 的身份验证方法,大概的流程是这样的:
Get和post的区别:
1、Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post提交的数据在HTTP包的请求包体中,对用户来说都是不可见的,相对安全。
2、Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
4、Get执行效率却比Post方法好。Get是form提交的默认方法。
Ge和post的选择:
1.私密性的信息请求使用post(如注册、登陆)。
2.查询信息使用get。
1.XSS攻击原理:
攻击者往Web页面里插入恶意 html标签或者javascript代码。
用来窃取cookie中的用户信息
解决:对一些输入的字符进行过滤,尽量采用post表单提交的方式。
2.CSRF攻击(跨站请求伪造):
登录受信任的网站A,并在本地生成Cookie,在不登出A的情况下,携带cookie去访问危险网站B
解决:通过验证码的方式解决
3.SQL注入攻击
就是通过吧SQL命令插入到Web表单递交或输入域名,最终达到欺骗服务器执行恶意的SQL命令。
解决:表单输入时通过正则表达式将一些特殊字符进行转换
html被称为超文本标记语言, 是一种描述性语言,用html 可以创建能在互联网上传输的信息页,是构成网页文档的主要语言,它是由很多的标签组成
xml 即可扩展标记语言,是Internet环境中跨平台的、依赖于内容的技术,是当前处理结构化文档信息的有力工具,满足了Web内容发布与交换的需要,适合作为各种存储与共享的通用平台。
都可以通过DOM 变成方式来访问。
都可以通过CSS来改变外观。
html和xml 都是标记语言,都是基于文本编辑和修改的。
xml不是要来取代html的,是对html的补充,用来与html协同工作的语言,基于上面这些优势,xml将来成为所有的数据处理和数据传输的常用工具非常可观。