!important > 行内样式 > id > class > tag > * >父元素继承 从右往左解析css
em 是一个相对长度单位,相对于其父元素的字体大小html
rem 相对根元素(html)的字体大小前端
vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度vue
浮动的框能够向左或向右移动,直到它的外边缘碰到包含框或另外一个浮动框的边框为止。因为浮动框不在文档的普通流中,因此文档的普通流中的块框表现得就像浮动框不存在同样。java
float:left | rightnode
static 元素框正常生成。块级元素生成一个矩形框,做为文档流的一部分,行内元素则会建立一个或多个行框,置于其父元素中。react
relative 元素框偏移某个距离。元素仍保持其未定位前的形状,它本来所占的空间仍保留。webpack
absolute 元素框从文档流彻底删除,并相对于其包含块定位。包含块多是文档中的另外一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在同样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。css3
fixed 元素框的表现相似于将 position 设置为 absolute,不过其包含块是视窗自己。es6
color 设置文本颜色
line-height 设置行高
text-align 对齐元素中的文本。
white-space 设置元素中空白的处理方式。
触发条件
应用
水平居中
垂直居中
水平垂直居中
...
语义化的HTML就是写出的HTML代码,符合内容的结构化(内容语义化),选择合适的标签(代码语义化),可以便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析。
常见的标签 nav header footer title main article
块级标签 div p ul li h1-h6
行内标签 span button i a img
Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。
注意,设为 Flex 布局之后,子元素的float、clear和vertical-align属性将失效。
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的全部子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。
属性决定主轴的方向(即项目的排列方向) flex-direction: row | row-reverse | column | column-reverse;
默认状况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,若是一条轴线排不下,如何换行。 flex-wrap: nowrap | wrap | wrap-reverse;
属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。 justify-content: flex-start | flex-end | center | space-between | space-around;
属性定义了项目在主轴上的对齐方式。 justify-content: flex-start | flex-end | center | space-between | space-around;
属性定义项目在交叉轴上如何对齐。align-items: flex-start | flex-end | center | baseline | stretch;
属性定义了多根轴线的对齐方式。若是项目只有一根轴线,该属性不起做用。align-content: flex-start | flex-end | center | space-between | space-around | stretch;
属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。order: ;
属性定义项目的放大比例,默认为0,即若是存在剩余空间,也不放大。flex-grow: ; /* default 0 */
属性定义了项目的缩小比例,默认为1,即若是空间不足,该项目将缩小。flex-shrink: ; /* default 1 */
属性定义了在分配多余空间以前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的原本大小 flex-basis: length | auto; /* default auto */
属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。flex: none | 'flex-grow' 'flex-shrink' || 'flex-basis' 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。 建议优先使用这个属性,而不是单独写三个分离的属性,由于浏览器会推算相关值。
属性容许单个项目有与其余项目不同的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,若是没有父元素,则等同于stretch。align-self: auto | flex-start | flex-end | center | baseline | stretch;
transition过渡
transition :css属性名称 过渡时间 时间曲线 延迟时间
animation动画
@keyframes
transition 和 animation 区别
this指向谁,要看怎么调用,常见的三种方式
Function.prototype.myCall = function (context) {
if (typeof this !== "function") {
throw new TypeError()
}
context = context || window
context.fn = this
const query = [...arguments].slice(1)
const result = context.fn(...query)
delete context.fn
return result
}
复制代码
Function.prototype.myBind = function (context) {
if (typeof this !== "function") {
throw new TypeError('bound is not callable')
}
var _this = this,
args = [...arguments].slice(1),
fNOP = function () {},
fBound = function () {
return _this.apply(this instanceof fBound ? this : context, [...args, ...arguments])
}
if (this.prototype) {
fNOP.prototype = this.prototype
}
fBound.prototype = new fNOP()
return fBound
}
复制代码
es5中做用域分为 全局做用域、局部做用域、eval做用域。局部做用域能向上访问到全局做用域,因此函数内部可以访问到函数外部的变量,反之不能。
var a = 10 // 全局做用域
(function(){
var b = 20 // 局部做用域
}())
eval("var c = 30") // eval做用域
复制代码
除了全局做用域, 每个做用域都是存在于某个做用域中的,在试图访问一个变量时JS引擎会从当前做用域开始向上查找直到Global全局做用域中止,变造成一条做用域链
执行环境定义了变量和函数有权访问的其余数据,每一个执行环境都有一个与之关联的变量对象(Variable Object)简称 VO,环境中定义的全部变量和函数都存储在这个变量对象中,全局执行环境是最外围的执行环境,在 web 浏览器中全局执行环境被认为 window 对象,函数都有本身的执行环境,当函数执行完成以后,执行环境随之销毁。当代码在环境中执行时,会建立一个变量对象的做用域链,做用域链的用途,是保证对执行环境有权访问的变量和函数的有序访问。做用域链的前端始终是当前代码执行的环境的变量对象,若是是函数,则将其活动对象(activation object)做为变量对象,下一个变量对象则是包含当前环境的外围环境,直到最后一个最外围的 window,标识符的解析是沿着做用域链一级一级的查找直到 window,这也是为何在函数内部能访问到函数外部的变量,而外部访问不到函数内部的缘由
当程序执行前,会建立全局环境以及全局VO对象,执行过程分为两个阶段:
变量初始化阶段
在VO对象中存储对应的函数参数、函数声明、变量声明
代码执行阶段
将声明后的变量赋值
简单的测试以下代码
alert(x) // function
var x = 10
alert(x) // 10
x= 20
function x(){}
alert(x) // 20
if(true) {
var a = 1
}else{
var b = 1
}
复制代码
第一步先看是否有函数参数传入,由于是全局环境,因此不会有函数参数
第二步看有没有函数声明,有则在 VO 对象中存储,值为 function
VO = {
x:function
}
复制代码
VO = {
x:function,
a:undefined,
b:undefined
}
复制代码
由于变量声明发生命名冲突会忽略,因此 x 仍是 function ,而后到了代码执行阶段,第一个 alert 能在 VO 中找到 x,因此是 function,然后 x=10 ,VO 中 x 的赋值为10,因此第二个 alert 为10,然后函数声明忽略,if 为 true,VO 中的 a 赋值为1,b 执行不到仍是 undefined
闭包是指有权访问另外一个函数做用域中的变量的函数,建立闭包的常见方式是,在一个函数内部建立另外一个函数。利用闭包技巧便可以访问到函数内部的变量,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
return f2
}
var func = f1()
func()
复制代码
以上代码,f2 定义在 f1 内部,因此 f2 的做用域链包含 f1 的活动对象,当 f1 调用完成后,f1 的执行环境的做用域链被销毁,可是 f2 仍然能访问到 f1 的活动对象,因此 f1 的活动对象还存在内存中,只有 f2 销毁,活动对象才销毁,咱们能够解除 func 的引用,即 func = null,通知垃圾回收机制回收,f2 的做用域链被销毁,f1 的活动对象也随之销毁
使用闭包的注意点
因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。
原型就是一个简单的对象,对象中包含一些方法,用于对象属性的继承,每一个对象都有个__proto__属性指向该对象的原型,以及constructor属性指向构造函数
javascrip函数都有一个prototype属性指向原型对象,原型对象也有一个__proto__属性,这个属性指向本身的原型,直到Object的原型(null),这样就造成了原型链,原型链主要用于实现继承和共享属性,避免资源浪费
function myNew(fn, ...args) {
let obj = {}
Object.setPrototypeOf(obj, fn.prototype);
let result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
function test(val) {
this.name = val
}
test.prototype.sayName = function () {
console.log(this.name)
}
var a = myNew(test, "ghc")
console.log(a)
a.sayName()
复制代码
function myInstanceof(left, right) {
let prototype = right.prototype
left = Object.getPrototypeOf(left)
while (true) {
if (left === null || typeof left !== "object") {
return false
}
if (left === prototype) {
return true
}
left = Object.getPrototypeOf(left)
}
}
复制代码
function superType() {
this.name = "ghc"
}
superType.prototype.sayName = function() {
alert(this.name)
}
function supType(name) {
superType.call(this,name)
}
supType.prototype = new superType()
supType.constructor = supType
var demo = new supType("ghc")
复制代码
组合继承是最经常使用的继承方式,但也有他的不足,不管什么状况下,组合继承都调用了两次超类型构造函数,第一次调用 superType 函数时,supType 的原型上指定了 name 属性,第二次在子类型构造函数调用,在实例上指定了 name 属性把原型上的属性覆盖了。咱们能够用寄生组合继承来解决这个问题
寄生组合继承就是借用构造函数来继承属性,用原型链的混成形式来继承方法
function Parent(name) {
this.name = name
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
var a = new Child("GHC")
console.log(a)
复制代码
ES6 容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
数组
let [a, b, c] = [1, 2, 3];
对象
let { foo, bar } = { foo: "aaa", bar: "bbb" };
字符串
const [a, b, c, d, e] = 'hello';
数字
let {toString: s} = 123;
布尔
let {toString: s} = true;
复制代码
数组扩展运算符(spread)是三个点(...)。它比如 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
对象的扩展运算符(...)用于取出参数对象的全部可遍历属性,拷贝到当前对象之中。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
// 等同于 {...Object(true)}
{...true} // {}
// 等同于 {...Object(undefined)}
{...undefined} // {}
// 等同于 {...Object(null)}
{...null} // {}
复制代码
只能放在参数的最后一位,不然会报错
数组扩展运算符能够与解构赋值结合起来,用于生成数组。
const [first, ...rest] = [1, 2, 3, 4, 5];
const [first, ...middle, last] = [1, 2, 3, 4, 5]; //err
对象
对象的解构赋值用于从一个对象取值,至关于将目标对象自身的全部可遍历的、但还没有被读取的属性,
分配到指定的对象上面。全部的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
复制代码
基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操做成功 */){
resolve(value);
} else {
reject(error);
}
});
// 不推荐写法
promise.then(function(value) {
// success
}, function(error) {
// failure
});
// 推荐写法
promise.then(function(value) {
// success
}).catch(function(error){
console.log('发生错误!', error);
});
复制代码
高级用法
1、 Promise.all
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。须要传入一个数组做为参数
const p = Promise.all([p1, p2, p3]);
复制代码
Promise.all 的两种状态:
只有当p一、p二、p3状态都变成fulfilled,p的状态才会变成fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数
只要p一、p二、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
2、Promise.race
Promise.race 用法与 Promise.all 相似,只要p一、p二、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
手写Promise
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function myPromise(fn) {
var that = this
that.state = PENDING
that.value = null
that.resolveCallballs = []
that.rejectCallballs = []
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.resolveCallballs.map(v => v(that.value))
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectCallballs.map(v => v(that.value))
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
myPromise.prototype.then = function (onFulfilled, onRejected) {
const that = this
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v
onRejected = typeof onRejected === "function" ? onRejected : r => {
throw r
}
if (that.state === PENDING) {
that.resolveCallballs.push(onFulfilled)
that.rejectCallballs.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
}).then(value => {
console.log(value)
})
复制代码
Generator有两个特征,一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,调用Generator函数,函数并不会执行,而是返回一个指向内部状态的指针对象,当调用next方法时才会执行函数,遇到yield函数会暂停,Generator异步编程用的比较少通常配合co模块或thunk函数,用的比较多的会是async
async 是es7异步编程的解决方案 ,是Generator函数的语法糖,默认返回Promise,函数内部若是遇到await会阻塞下面的代码,跳出函数继续执行宏任务中的代码
class能够看做是构造函数的语法糖,constructor方法就是构造方法,this指向实例对象
class A {
constructor() {
this.name = "ghc"
}
}
class B extends A {
constructor() {
super()
}
}
var b = new B()
console.log(b)
复制代码
ES5 的对象属性名都是字符串,这容易形成属性名的冲突,Symbol能够保证每一个属性的名字都是独一无二的
let mySymbol = Symbol();
let a = {};
a[mySymbol] = 'Hello!';
let s = Symbol();
let obj = {
[s]() { ... }
};
复制代码
ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
复制代码
Set 实例的属性和方法
属性:
Set 实例的方法分为两大类:操做方法(用于操做数据)和遍历方法(用于遍历成员)
操做方法:
遍历方法:
JavaScript 的对象(Object),本质上是键值对的集合(Hash结构),可是传统上只能用字符串看成键。这给它的使用带来了很大的限制。ES6 提供了 Map 数据结构。它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
复制代码
模块化的好处是防止变量冲突、提升代码复用性、提升代码可维护性
一、当即执行函数
当即执行函数是模块化经常使用的手段
(function(globalVariable){
globalVariable.test = function() {}
// ... 声明各类变量、函数都不会污染全局做用域
})(globalVariable)
复制代码
二、AMD 和 CMD
使用较少
三、commonJS
最先Node中使用,webpack中也常常看到
// a.js
module.exports = {
a: 1
}
// or
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> log 1
复制代码
四、ES Module
ES Module 是原生实现的模块化方案
// 引入模块 API
import XXX from './a.js'
import { XXX } from './a.js'
// 导出模块 API
export function a() {}
export default function() {}
复制代码
更多查看阮一峰老师的 ECMAScript 6 入门 es6.ruanyifeng.com/#README
document.getElementById()
document.getElementsByClassName()
document.getElementsByTagName()
document.getElementsByName()
document.querySelector()
document.createElement()
document.createTextNode()
node.cloneNode()
parent.appendChild(child)
parent.insertBefore(newNode,refNode)
parent.removeChild(node) 通常用法 node.parentNode.removeChild(node)
parent.replaceChild(newNode,oldNode)
父关系API
parentNode 返回父节点,父节点多是 element、document、DocumentFragment
parentElement 返回父节点,与 parentNode 区别是父元素只能是 element,不然返回 null
子关系API
children 返回一个实时的 HTMLCollection ,子节点都是Element,IE9如下浏览器不支持
childNodes 返回一个实时的 NodeList ,表示元素的子节点列表,注意子节点可能包含文本节点、注释节点等
firstChild 返回第一个子节点,不存在返回null,与之相对应的还有一个 firstElementChild
lastChild 返回最后一个子节点,不存在返回null,与之相对应的还有一个 lastElementChild
兄弟关系型API
previousSibling 节点的前一个节点,若是不存在则返回null。注意有可能拿到的节点是文本节点或注释节点
nextSibling :节点的后一个节点,若是不存在则返回null。注意有可能拿到的节点是文本节点
previousElementSibling :返回前一个元素节点,前一个节点必须是Element,注意IE9如下浏览器不支持。
nextElementSibling :返回后一个元素节点,后一个节点必须是Element,注意IE9如下浏览器不支持。
属性型API
setAttribute(name,value)
name是特性名,value是特性值。若是元素不包含该特性,则会建立该特性并赋值
getAttribute(name)
getAttribute返回指定的特性名相应的特性值,若是不存在,则返回null
样式相关API
node.style.xxx 只能获取到内联样式
window.getComputedStyle(node,pseudo-element) 第二个参数是伪类,能够获取应用到元素上的全部样式,IE8或更低版本不支持此方法
node.getBoundingClientRect() 用来返回元素的大小以及相对于浏览器可视窗口的位置
http请求
请求分为两种 get 和 post
http状态码
捕获由外向内,冒泡由内向外 event.stopPropagation()阻止事件的冒泡
协议、域名、端口有一个不一样的就是跨域,ajax请求就会失败
兼容性不错,但只限get请求
手写jsonp
function myjsonp(url, data) {
return new Promise((resolve, reject) => {
let dataString = url.indexOf('?') === -1 ? '?' : '&'
if (data instanceof Object) {
for (let key in data) {
dataString += key + '=' + data[key] + '&'
}
}
const callBackName = Math.random().toString().replace('.', '')
dataString += "callback" + callBackName
let el = document.createElement("script")
el.src = url + dataString
el.async = true
window[callBackName] = function (v) {
resolve(v)
document.body.removeChild(el)
}
document.body.appendChild(el)
})
}
复制代码
主要由后端实现
cookie
通常由服务器生成,能够设置过时时间 大小4K
sessionStorage
页面关闭就清理 通常大小5m
LocalStorage
除非被清理,不然一直存在 通常大小5m
indexDB
除非被清理,不然一直存在 大小无限
Service Worker
Service Worker 是运行在浏览器背后的独立线程,通常能够用来实现缓存功能 (去了解PWA)
将html转换为dom树 css转换为cssom 而后合成为render树 浏览器根据render树进行布局(回流)
事件循环是指: 执行一个宏任务,而后执行清空微任务列表,循环再执行宏任务,再清微任务列表
垃圾回收: 将内存中再也不使用的数据进行清理,释放出内存空间。V8 将内存分红 新生代空间 和 老生代空间。
新生代空间: 用于存活较短的对象
老生代空间: 用于存活时间较长的对象
性能优化能够经过5个方面入手,首先会同个 DNS 解析域名转换相应的 IP 地址,而后根据 IP 地址找到服务器进行 TCP 连接,这两个方面前端能作的很少,而后是客户端发送 HTTP 请求和服务端响应的过程,这点前端能作的是整合资源包的大小,而后浏览器拿到数据,进行渲染。总的来讲,前端能够经过整合资源包的大小和优化渲染过程两个方面入手。
整合资源包的大小可使用按需加载,好比 Vue 路由可使用路由懒加载防止打包出来的资源包过大,同时也能够对图片进行压缩优化,JPG 压缩后图片清晰度不会变化太大适合一些大图,轮播图;PNG 体积较大,线条细腻,适合小图 logo,项目中尽可能使用字体图标去替代图片,或者用 base64 / 缓存图片也能够减小网络请求。
而后前端还能够经过优化渲染过程方面入手,首先 HTML 生成 dom树,css 生成样式规则,二者结合生成 render 树,而后调用 GPU 去渲染页面。css 方面的优化要避免选择器的嵌套,由于选择器规则是从右到左去匹配的,嵌套就会增长开销。避免使用通配符,它会匹配全部元素。
另外要防止 css 阻塞,只有样式规则完成,页面才能去渲染,这也是为何css要放在 head 头里面。防止 js 阻塞,在渲染的过程若是遇到 js 脚本,控制权会从渲染引擎到 js 引擎阻塞页面的渲染。因此脚本要写在最后,script 标签能够用 defer 和 async 异步加载。另外还要减小 dom 的访问和操做,这是 js 引擎和渲染引擎之间的通讯,很是消耗性能
图片可分为位图和矢量图 常见的位图有JPG、PNG、GIF,矢量图有SVG,项目中尽可能使用字体图标或SVG替代图片,使用Base64也能够减小网络请求
JPG
大的背景图,轮播图
png
体积大 用在小logo 透明背景
GIF
动态图片
雪碧图
小图太多的时候,集中成一张图片减小 HTTP 请求,随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台
SVG
能适应不一样设备且画质不能损坏的图片
使用cach-control或expires这类强缓存时,缓存不过时的状况下,不向服务器发送请求。强缓存过时时,会使用last-modified或etag这类协商缓存,向服务器发送请求,若是资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;若是资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应
一、避免使用层级较深的选择器,或其余一些复杂的选择器,以提升CSS渲染效率
二、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不只仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要从新计算一次
三、元素适当地定义高度或最小高度,不然元素的动态内容载入时,会出现页面元素的晃动或位置,形成回流
四、给图片设置尺寸。若是图片不设置尺寸,首次载入时,占据空间会从0到彻底出现,上下左右均可能位移,发生回流
五、不要使用table布局,由于一个小改动可能会形成整个table从新布局。并且table渲染一般要3倍于同等元素时间
六、对于一些进行动画的元素,使用硬件渲染,从而避免重绘和回流
使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
节流( Throttle )
function throttle(fn, interval) {
// last为上一次触发回调的时间
let last = 0
// 将throttle处理结果看成函数返回
return function () {
let context = this
let args = arguments
let now = +new Date()
if (now - last >= interval) {
last = now;
fn.apply(context, args);
}
}
}
// 用throttle来包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
document.addEventListener('scroll', better_scroll)
复制代码
防抖( debounce )
// fn是咱们须要包装的事件回调, delay是每次推迟执行的等待时间
function debounce(fn, delay) {
// 定时器
let timer = null
// 将debounce处理结果看成函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 每次事件被触发时,都去清除以前的旧定时器
if(timer) {
clearTimeout(timer)
}
// 设立新定时器
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
// 用debounce来包装scroll的回调
const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000)
document.addEventListener('scroll', better_scroll)
复制代码
用 Throttle 来优化 Debounce
// fn是咱们须要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {
// last为上一次触发回调的时间, timer是定时器
let last = 0, timer = null
// 将throttle处理结果看成函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 记录本次触发回调的时间
let now = +new Date()
// 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
if (now - last < delay) {
// 若是时间间隔小于咱们设定的时间间隔阈值,则为本次触发操做设立一个新的定时器
clearTimeout(timer)
timer = setTimeout(function () {
last = now
fn.apply(context, args)
}, delay)
} else {
// 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应
last = now
fn.apply(context, args)
}
}
}
// 用新的throttle包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
document.addEventListener('scroll', better_scroll)
复制代码
<script>
// 获取全部的图片标签
const imgs = document.getElementsByTagName('img')
// 获取可视区域的高度
const viewHeight = window.innerHeight || document.documentElement.clientHeight
// num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出
let num = 0
function lazyload(){
for(let i=num; i<imgs.length; i++) {
// 用可视区域高度减去元素顶部距离可视区域顶部的高度
let distance = viewHeight - imgs[i].getBoundingClientRect().top
// 若是可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出
if(distance >= 0 ){
// 给元素写入真实的src,展现图片
imgs[i].src = imgs[i].getAttribute('data-src')
// 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出
num = i + 1
}
}
}
// 监听Scroll事件
window.addEventListener('scroll', lazyload, false);
</script>
复制代码
一、按需加载,使用路由懒加载提升首次打开的速度
父子组件通讯是单向数据流的,父组件经过props转递数据给子组件,子组件不能修改props,只能经过emit触发父组件的方法通知父组件修改数据
对于这种状况能够经过查找父组件中的子组件实现,也就是 this.children,在 $children 中能够经过组件 name 查询到须要的组件实例,而后进行通讯。
provide / inject,官方文档中不推荐直接使用在业务中,可使用 vuex 作状态管理
mixin 全局混入,官方不推荐使用,会影响到每一个组件实例,一般插件都是这样作初始化的。
mixins 封装多个组件相同的逻辑
computed 是计算属性,依赖其余属性计算值,而且computed的值有缓存,访问其值时不会每次计算,只有当计算值变化才会返回内容。
watch 监听到值的变化就会执行回调,在回调中能够进行一些逻辑操做。
防止组件屡次渲染,keep-alive有两个独有的生命周期 activated、deactivated
v-show 通常用于切换较为平凡的场景,切换开销较小
v-if 通常用于切换较少的场景,切换开销较大
响应式原理:首先会遍历 option 里的 data ,添加读取器属性到实例里来作一层代理,这也是为何可以经过 this 访问到 data 里的数据。而后会调用 observe 设置监听器,observe 主要是遍历 data 中的属性,将其修改成读取器属性,而且生成各自的订阅器。在 get 中进行依赖收集,把订阅者 watcher 添加到订阅器中,并返回属性的值。在 set 中修改数据的值,而且调用订阅者的 update 方法修改文本,这样监听器就设置完成。接着会遍历 dom 节点,将节点中的数据替换,并生成订阅者,触发监听器进行依赖收集。这样当数据发生改变,set 方法就会触发订阅器,订阅器就会通知全部的订阅者更新视图。
class Vue {
constructor(options) {
this._data = options.data;
observer(this._data);
/* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
new Watcher();
/* 在这里模拟render的过程,为了触发test属性的get函数 */
console.log('render~', this._data.test);
}
}
class Dep {
constructor() {
/* 用来存放Watcher对象的数组 */
this.subs = [];
}
/* 在subs中添加一个Watcher对象 */
addSub(sub) {
this.subs.push(sub);
}
/* 通知全部Watcher对象更新视图 */
notify() {
this.subs.forEach((sub) => {
sub.update();
})
}
}
class Watcher {
constructor() {
/* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */
Dep.target = this;
}
/* 更新视图的方法 */
update() {
console.log("视图更新啦~");
}
}
function observer(value) {
if (!value || (typeof value !== 'object')) {
return;
}
Object.keys(value).forEach((key) => {
defineReactive(value, key, value[key]);
});
}
function defineReactive(obj, key, val) {
/* 一个Dep类对象 */
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
/* 将Dep.target(即当前的Watcher对象存入dep的subs中) */
dep.addSub(Dep.target);
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
/* 在set的时候触发dep的notify来通知全部的Watcher对象更新视图 */
dep.notify();
}
});
}
let o = new Vue({
data: {
test: "I am test."
}
});
o._data.test = "hello,world.";
复制代码
跳转方法:
this.$router.push()
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
this.$router.replace()
replace跳转不会保留历史痕迹,其余用法和 push 一致
state:存放变量
mutation:修改 state 里面的变量
getter:获取 state 里的值
action:异步修改 state 的值,也可用来封装多个 mutation