防抖:触发事件后,在n秒内,事件只执行一次,如果在n秒内又触发了事件,则会重新计算函数的执行时间。
比如点击按钮,2秒后调用函数,结果在1.5秒的时候又点了,则会重新计算2秒后在调用函数。
应用场景:下拉触底加载下一页。
节流:连续发生的事件在n秒内,只执行为一次
应用场景比较多的是:搜索查询
什么是闭包:闭包就是能够读取其他函数内部变量的函数
function fn0 () {
const aaa = 0
return function() {
return aaa
console.log('打印', aaa)
}
}
闭包存在意义:
可以延长变量的生命周期4可以创建私有的环境
闭包好处:
可以读取其他函数的内部变量
将变量始终保存在内存中
可以封装对象的私有属性和方法
坏处:
消耗内存、使用不当会造成内存溢出问题
Vue 中的 data 必须是个函数,因为当 data 是函数时,
组件实例化的时候这个函数将会被调用,返回一个对象,
计算机会给这个对象分配一个内存地址,实例化几次就分配几个内存地址,
他们的地址都不一样,所以每个组件中的数据不会相互干扰,改变其中一个组件的状态,其它组件不变。
简单来说,就是为了保证组件的独立性和可复用性,如果 data 是个函数的话,
每复用一次组件就会返回新的 data,类似于给每个组件实例创 建一个私有的数据空间,
保护各自的数据互不影响
MVC:M(model数据)、V(view视图),C(controlle控制器)
缺点是前后端无法独立开发,必须等后端接口做好了才可以往下走;
前端没有自己的数据中心,太过依赖后台
MVVM:M(model数据)、V(view视图)、VM(viewModel控制数据的改变和控制视图)
html部分相当于View层,可以看到这里的View通过通过模板语法来声明式的将数据渲染进DOM元素,
当ViewModel对Model进行更新时,通过数据绑定更新到View。
Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,
即Model变化时VIew可以实时更新,View变化也能让Model发生变化
MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,
也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,
而是改变属性后该属性对应View层显示会自动改变
是采用数据劫持结合发布者-订阅者模式的方式,
通过Object.defineProperty()来劫持各个属性的setter,getter,
在数据变动时发布消息给订阅者,触发相应的监听回调从而达到数据和视图同步。
实际上就是一个闭包,因为vue是单页面应用,是由很多组件构成,每一个组件中都有一个data,
所以通过闭包给每一个组件创建了一个私有的作用域,这样就不会相互影响。
v-if是通过添加和删除元素来进行显示或者隐藏
v-show是通过操作DOM修改display样式来修改元素的显示和隐藏
如果需要频繁的进行元素的显示和隐藏使用v-show性能更好
key 可以提高虚拟DOM的更新效率。
在vue中,默认“就地复用”的策略,在DOM操作的时候,如果没有key 就会造成选项错乱
key 只能是字符串或者number,其他类型不可以
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,
Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新的虚拟DOM】与【旧的虚拟DOM】差异比较比较规则如下:
2. 比较规则:
1)旧虚拟DOM找到了与新虚拟DOM相同的key:
若虚拟DOM中内容没变,直接使用之前的真实DOM
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
3. 用index作为key可能会引发的问题:
1)若对数据进行:逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新==>界面效果没问题,但效率低
2)如果结构中还包含输入类的DOM,会产生错误的DOM更新 ==> 界面有问题
通过在子组件身上绑定自定义属性,然后再子组件里使用props属性来接收即可
1)第一种方式:通过父组件给子组件传递函数类型的props实现:子组件给父组件传递数据
父组件:
2)第二种方式:通过父组件给子组件绑定一个自定义事件实现:子组件给父组件传递数据
父组件
3)第三种方式:通过父组件给子组件绑定一个自定义事件实现:使用ref实现
父组件
main.js:将全局事件bus,挂载到Vue的原型上,这样所有的组件都可以使用
兄弟组件A:
兄弟组件B:
4. 消息订阅与发布
一种组件间的通信方式,适用于任意组件间通信。
使用步骤:
mounted() {
this.pid = punsub.subscribe('xxx', (data)=>{
......
})
}
不管是普通函数还是构造函数,都有一个属性prototype,用来指向这个函数的原型对象。
同时原型对象也有一个constructor属性,用来指向这个原型对象的构造函数。
原型对象的作用:用来存储实例化对象的公有属性和方法
例如
function Person (name, age) {
this.name = name
this.age = age
this.say = function () {
console.log('我会说话')
}
}
let person1 = new Person('小明', 20)
console.log(person1.name) // 小明
console.log(person1.age) // 20
console.log(person1.say()) // 我会说话
如上:实例化出来的person1,就可以使用构造函数Person里面的属性和方法
总结
具体可参考下方文章链接地址(写的非常详细易懂):https://blog.csdn.net/weixin_56505845/article/details/119683904
浅拷贝
创建一个对象,这个对象有着原始对象的值的精确拷贝。
这里要引入基本数据类型 和 引用数据类型 ,浅拷贝的如果是基本数据类型,则拷贝的直接是这个数据的值;如果是引用类型,则是创建了一个新的指针,用来指向原类型的内存地址(即拷贝了这个内存地址),如果一个对象改变了,则会影响另一个对象
深拷贝
与之相对,深拷贝指的是创建一个新的指针,同时也会生成一个新的内存地址,这个新的指针指向新的内存地址。与原对象是相互独立,互不影响
实现深拷贝的方法
JSON.parse(JSON.stringiFy())
function clone(target) {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = target[key];
}
return cloneTarget;
};
创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回新对象。
考虑到要拷贝的对象可能嵌套很深,因此可以通过递归的方法来实现,优化上方代码:
1. 如果是原始类型,无需拷贝,可以直接返回
2. 如果是引用类型,可以创建一个新的对象,遍历需要克隆的对象,将需要克隆对象执行深拷贝后依次添加到新对象上
module.exports = function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
详细可参考:https://www.jianshu.com/p/a447d7325736
首先我们要先明白一个道理:Vue的响应式并不是数据发生变化后,DOM立即跟着发生变化的,而是按一定的策略进行DOM更新的。
作用:$nextTick 是在下次 DOM 更新循环结束之后执⾏延迟回调,在修改数据之后使⽤ $nextTick,则可以在回调中获取更新后的 DOM,在下次 DOM 更新循环结束之后执行延迟回调
说白了就是:当数据更新了,在DOM更新后,自动执行该函数
什么时候用?
想了解更详细的️参考:https://www.jianshu.com/p/8efa5cba7d07
可点击参考文档详细了解
var: 存在变量提升;存在变量覆盖,已经被定义且赋值的变量,如果再次被赋值,则以后一次值为准;没有块级作用域;
const: 定义的是常量,声明之后必须赋值;定义的值不能去修改,否则报错;有块级作用域;不存在变量提升和变量覆盖;对于数组和对象的元素修改,不算做对常量的修改,不会报错。
let: 有块级作用域;不存在变量提升和变量覆盖;let不允许在相同的作用域中重复声明,注意是相同作用域,不同作用域重复声明不会报错
let a = 1; let b = 2; 如果在不声明第三个变量的前提下,使a=2, b=1?
答案:[a, b] = [b, a]
let arr = [23, 12, 13, 33, 22, 12, 21]
let item = [...new Set(arr)]
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
答案:1,2,4,3
解释:以上考察的是关于promise的原理,promise的构造函数是同步执行的,当new Promise的一瞬间,1,2 就立刻被执行,而 .then方法是异步执行的,当执行完1和2之后,会执行输出4,最后执行输出3
跨域:只要协议、域名和端口号有一个不相同就会产生跨域问题。同源策略是一个安全策略。同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
解决办法:
devServer: {
proxy: { //配置跨域
'/api': {
target: 'http://121.121.67.254:8185/', //这里后台的地址模拟的;应该填写你们真实的后台接口
changOrigin: true, //用于控制请求头中的post值,默认开启
pathRewrite: {
/* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/api/core/getData/userInfo 时
实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
*/
'^/api': ''
}
},
}
}
方案1 *:通配符,全部允许,存在安全隐患(不推荐)。
一旦启用本方法,表示任何域名皆可直接跨域请求:
1 server {
2 ...
3 location / {
4 # 允许 所有头部 所有域 所有方法
5 add_header 'Access-Control-Allow-Origin' '*';
6 add_header 'Access-Control-Allow-Headers' '*';
7 add_header 'Access-Control-Allow-Methods' '*';
8 # OPTIONS 直接返回204
9 if ($request_method = 'OPTIONS') {
10 return 204;
11 }
12 }
13 ...
14 }
方案2:多域名配置(推荐)
配置多个域名在map中 只有配置过的允许跨域:
map $http_origin $corsHost {
2 default 0;
3 "~https://zzzmh.cn" https://zzzmh.cn;
4 "~https://chrome.zzzmh.cn" https://chrome.zzzmh.cn;
5 "~https://bz.zzzmh.cn" https://bz.zzzmh.cn;
6 }
7 server {
8 ...
9 location / {
10 # 允许 所有头部 所有$corsHost域 所有方法
11 add_header 'Access-Control-Allow-Origin' $corsHost;
12 add_header 'Access-Control-Allow-Headers' '*';
13 add_header 'Access-Control-Allow-Methods' '*';
14 # OPTIONS 直接返回204
15 if ($request_method = 'OPTIONS') {
16 return 204;
17 }
18 }
19 ...
20 }
npm i -S webpack-dev-middleware 安装中间键,把前端和后端的服务绑在一起
中间件
let webpack = require('webpack')
let middle = require('webpack-dev-middleware')
let compiler = webpack(require('./webpack.config.js'))
app.use(middle(compiler))
var allowCrossDomain = function(req,res,next) {
// 请求源
res.header("Access-Control-Allow-Origin", "*")
// 请求头 token
res.header("Access-Control-Allow-Headers", "*")
// 请求方法 get post put del
res.header("Access-Control-Allow-Methods", "*")
next();
}
app.use(allowCrossDomain )
1. git init 初始化git仓库 (mac中Command+Shift+. 可以显示隐藏文件)
2. git status 查看文件状态
3. git add 文件列表 追踪文件
4. git commit -m 提交信息 向仓库中提交代码
5. git log 查看提交记录
(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
git reset --soft HEAD~n
get是从服务器上获取数据,post是向服务器传送数据。
POST比GET安全,因为数据在地址栏上不可见。
get方式提交的数据最多只能有1024字节,而post则没有此限制。
GET使用URL或Cookie传参。而POST将数据放在request BODY中。
GET与POST都有自己的语义,不能随便混用。
据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基,本可以无视。而在网 络环境差的情况下,两次包的TCP在验证数据包完整 性上,有非常大的优点。post 发送两次,get 只发送一次。
共同点: 都是保存在浏览器端、且同源的
不同点:
cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
存储大小限制也不同:
cookie数据不能超过4K,sessionStorage和localStorage可以达到5M
sessionStorage:仅在当前浏览器窗口关闭之前有效;
localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;
cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
作用域不同
sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;
localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在
cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在
区别:
async是来定义函数的,定义异步函数,打印函数名可以得到一个promise对象,言外之意可以通过这个 函数名称.then 这个方法
await 后面跟的是任意表达式,一般使用promise的表达式
async 内部实现,有返回值 成功返回promise.resolve() ,出错返回promise.reject() 返回值用catch捕获
await 等待后面的promise对象执行完毕,拿到了promise.resolve()的值之后,执行后面的代码。await后面的表达式,能是promise.reject(),所以建议await放在try…catch语句中
优点:async和await 属于es7语法。编写方便,提高程序效率,避免了回调地狱
补充:promise和async和await的区别
promise es6语法,promise中包含catch,async需要自己定义catch
promise 提供的方法会多一些,all、race等方法,aync中是没有的。
setTimeout,如果时间为0,则会立即插入队列,不是立即执行,等待前面的代码执行完毕。
function getMaxArryNum(arr) {
return Math.max(...arr)
}
getMaxArryNum([1,2,3,4,5,6])
const getMinArryNum= (arr) => {
return Math.min(...arr)
}
getMinArryNum([1,2,3,4,5,6])
const removeEqual = (arr) => {
const result = arr.filter((item, index, self) => {
return self.indexof(item) === index
})
return result
}
removeEqual([1, 2, 2, 3, 4, 5, 5, 6, 7, 7])
第一次循环,传入元素1,index(1)的索引为0,而此时1的索引本来就是0,OK,满足。
第二次循环,传入元素2,index(2)的索引为1,而此时2的索引也是1,OK,也满足。
第三次循环,传入元素2,index(2)的索引为1,而此时2的索引为2,OK,不满足,被PASS,这里就是巧妙的借用了indexOf始终查找到第一次出现的位置。
总结
filter(x,index,self)可以为数组提供过滤功能,其中x代表元素,index是与X一同传入元素的索引,而self代表数组本身。
14. 生成从0 到 指定数字的数组
先对数组进行排序,再循环,如果相邻的两项相同,则删除一项,i–,再继续对比。
var arr = [1, 2, 3, 2, 4, 1];
arr.sort();
for (var i = 0; i < arr.length; i++) {
if (arr[i] === arr[i+1]) {
arr.splice(i,1);
i--;
}
}
console.log(arr); // [1, 2, 3, 4]
var arr = [1, 2, 3, 2, 4, 1];
var newArr = new Set(arr);
console.log(newArr); // [1, 2, 3, 4];
最简单的思路,先创建一个新数组作为容器,遍历原数组,判断每一项在新数组中是否存在,若不存在则把这一项push到新数组中,若存在则忽略。
var arr = [1, 2, 3, 2, 4, 1];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4]
利用对象的属性名不能重复这一特性,创建一个对象,把数组中的值依次添加为对象的属性,再使用Object.keys()得到包含所有属性名的数组。但要注意对象的属性名都是字符串形式,所以在本例中还要把它们转回数字。
var arr = [1, 2, 3, 2, 4, 1];
var obj = {};
var res = [];
arr.forEach(n => obj[n] = 1); // 把每一项添加为对象的属性,重复的属性不会再次添加,而是修改已存在的属性的值
res = Object.keys(obj).map(n => +n); // 得到包含字符串属性名的数组并把每一项转换成数字
console.log(res); // [1, 2, 3, 4]
const arrSum = (arr) => {
const temp = arr.reduce((pre, now) => {
return pre+now
},0)
return temp
}
arrSum([1,2,3,4])
const getArr = (startNum, endNum) => {
let arr = []
for(var i=startNum; i<=endNum; i++){
arr.push(i)
}
return arr
}
getArr(0,4)
js 数据类型分为基本数据类型和复杂数据类型
基本数据类型:Boolean、Number、String、Null、Undefined
复杂数据类型: Object、Array、Function、Date
在js中,变量和函数的声明会被提升到最顶部执行
函数提升高于变量的提升
函数内部如果用var声明了相同名称的外部变量,函数将不再向上寻找
匿名函数不会提升
this总是指向函数的直接调用者。
如果有new关键字,this指向new出来的对象
在事件中,this指向触发这个事件的对象
forEach方法,是最基本的方法,遍历和循环。默认有3个参数:分别是遍历的每一个元素item,遍历的索引index,遍历的数组array
map方法,和foreach一致,不同的是会返回一个新的数组,所以callback需要有return返回值,如果没有会返回undefined
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
不可以当作构造函数,也就是说不可以使用new命令,否则会报错
不可以使用arguments对象,该对象在函数体内不存在,如果要用可以使用Rest参数代替
不可以使用yield命令,因此箭头函数不能用作Generator函数
新增模版字符串
箭头函数
增加let、const来声明变量
for-of用来遍历数据-例如数组中的值
解构赋值
新增简单数据类型Symbol,独一无二的,不会与其他属性名冲突
将Promise对象纳入规范,提供了原生的Promise对象
map 循环遍历数组、返回一个新的数组
forEach 循环遍历数组,不改变原数组
push/pop 在数组的末尾添加/删除元素 改变原数组
unshift/ shift 在数组的头部添加/删除元素,改变原数组
join 把数组转化为字符串
some 有一项返回为true,则整体为true
every 有一项返回为true,则整体为false
filter 数组过滤
slice(start, end) 数组截取,包括开头,不包括截取,返回一个新的数组
splice(start, number, value) 删除数组元素,改变原数组
indexof/lastindexof: 查找数组项,返回对应的下标
concat:数组的拼接,不影响原数组,浅拷贝
sort:数组排序 改变原数组
reverse: 数组反转,改变原数组
动态加载组件
减少http请求
减少DNS查询
使用CDN
避免重定向
图片懒加载
路由懒加载
减少DOM元素操作
使用外部js和css
压缩js、css、字体、图片等
使用iconfont字体图标、雪碧图等
避免图片的src为空
把样式表放在link中
把js放在页面的底部