1、this.$router.back(); //返回上一级
2、父组件可以使用 props 把数据传给子组件。
3、子组件可以使用 $emit 触发父组件的自定义事件。
Moment (超常用,时间转换)
1、将时间转换为时间戳(每次这么简单,我为啥记不住)
moment(start_time).valueOf() / 1000
2、只有时间可以转换为moment对象(反显在组件上面)
[moment(start_time), moment(end_time)]
[moment( 2019-12-18 14:27:38), moment( 2019-12-18 14:27:38)]
3、将时间戳转为时间
moment(item.deal_info.time * 1000).format("YYYY-MM-DD HH:mm:ss")
4、将组件选择的当前时间转为当天的零点(难受)
moment(moment(date[0]).format("YYYY-MM-DD 00:00:00")).valueOf() / 1000
5、将时分秒格式为时间戳
//将选中的时间初始为当天的凌晨
export function initFormatMorning(data) {
return moment(moment(data).format("YYYY-MM-DD 00:00:00")).valueOf() / 1000;
}
// 将选中的时间初始为者当天的最后时分秒
export function initFormatEvening(data) {
return moment(moment(data).format("YYYY-MM-DD 23:59:59")).valueOf() / 1000;
}
//将时间戳格式为年月日,时分秒
export function formatTime(data) {
return moment(data * 1000).format("YYYY-MM-DD HH:mm:ss");
}
/将时分秒直接格式为时间戳
export function timeStamp(data) {
let delDataTime =
moment(
moment()
.subtract("days", 0)
.format("YYYY-MM-DD 00:00:00")
).valueOf() / 1000; 当天的零点
return (
moment(moment(data).format("YYYY-MM-DD HH:mm:ss")).valueOf() / 1000 -
delDataTime
);
}
模糊搜索:
1、先树,(正确)删除,(正确);再在上面加下拉框的,正确;删除(正确),
2、下拉框)(不正确),删除不正确;再在上面加树正确,再删除,有些正确,有些有问题
上面的都没有问题了,现在的问题是,如何实时刷新树组件
笔记
2、编辑或新增的时候,必填的判断(不需要多个if)
const massageRemind = [
{ key: "name", title: "请填写名称" },
{ key: "service_city_id", title: "请选择所在市县" },
{ key: "address", title: "请请选择基地地址" },
{ key: "place_pics", title: "请上传基地图片" }
];
for (let index in massageRemind) {
if (!this.formData[massageRemind[index].key]) {
this.$Msg.error(massageRemind[index].title);
return;
}
}
demo
1、表格里面的操作,全部分离出来
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当钩子执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
},
/相当于是构造函数
//根据屏幕获取宽高
export function getCategory(screenWidth, self) {
const screenData = [
{
greater_than: 0,
less_than: 1300,
key: "inline_xl",
value: 10
},
{
greater_than: 1299,
less_than: 1400,
key: "inline_xl",
value: 9
},
{
greater_than: 1399,
less_than: 1500,
key: "inline_xl",
value: 8
},
{
greater_than: 1499,
less_than: 1600,
key: "inline_xl",
value: 7
},
];
screenData.map(item => {
if (screenWidth > item.greater_than && screenWidth < item.less_than) {
self[item.key] = item.value;
}
});
}
Webstorm快捷键
1、全局替换:ctrl+shift+r
2、快速定位页码:
界面操作
快捷键 说明 ctrl+shift+N 通过文件名快速查找工程内的文件(必记) ctrl+shift+alt+N 通过一个字符快速查找位置(必记) ctrl+F 在文件内快速查找代码 F3 查找下一个 shift+F3 查找上一个 ctrl+R 文件内代码替换 ctrl+shift+R 指定目录内代码批量替换 ctrl+shift+F 指定目录内代码批量查找 ctrl+R 文件内代码替换
快捷键 说明 ctrl+shift+A 快速查找并使用编辑器所有功能(必记) alt+[0-9] 快速拆合功能界面模块 ctrl+shift+F12 最大区域显示代码(会隐藏其他的功能界面模块) alt+shift+F 将当前文件加入收藏夹 ctrl+alt+s 打开配置窗口 ctrl+tab 切换代码选项卡(还要进行此选择,效率差些) alt+<-或-> 切换代码选项卡 界面操作
快捷键 说明 ctrl+shift+N 通过文件名快速查找工程内的文件(必记) ctrl+shift+alt+N 通过一个字符快速查找位置(必记) ctrl+F 在文件内快速查找代码 F3 查找下一个 shift+F3 查找上一个 ctrl+R 文件内代码替换 ctrl+shift+R 指定目录内代码批量替换 ctrl+shift+F 指定目录内代码批量查找 ctrl+R 文件内代码替换 代码编辑
快捷键 说明 ctrl+shift+A 快速查找并使用编辑器所有功能(必记) alt+[0-9] 快速拆合功能界面模块 ctrl+shift+F12 最大区域显示代码(会隐藏其他的功能界面模块) alt+shift+F 将当前文件加入收藏夹 ctrl+alt+s 打开配置窗口 ctrl+tab 切换代码选项卡(还要进行此选择,效率差些) alt+<-或-> 切换代码选项卡 ctrl+F4 关闭当前代码选项卡 导航
快捷键 说明 ctrl+D 复制当前行 ctrl+W 选中单词 ctrl+<-或-> 以单词作为边界跳光标位置 alt+Insert 新建一个文件或其他 ctrl+alt+L 格式化代码 shift+tab/tab 减少/扩大缩进(可以在代码中减少行缩进) ctrl+Y 删除一行 shift+enter 重新开始一行(无论光标在哪个位置) 建议配置版本控制快捷键
快捷键 说明 esc 进入代码编辑区域 alt+F1 查找代码在其他界面模块的位置,颇为有用 ctrl+G 到指定行的代码 ctrl+]/[ 光标到代码块的前面或后面 alt+up/down 上一个/下一个方法
快捷键 说明 ctrl+C 提交代码 ctrl+p 向远程版本库推送更新 ctrl+G 到指定行的代码 ctrl+]/[ 光标到代码块的前面或后面 alt+up/down 上一个/下一个方法
浏览器
User-Agent (用户代理)
User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。(标识浏览器身份)
一些网站常常通过判断 UA 来给不同的操作系统、不同的浏览器发送不同的页面,因此可能造成某些页面无法在某个浏览器中正常显示,但通过伪装 UA 可以绕过检测。
这个都是浏览器的伪装,JS是不能实现问题,不能解决网页端实现手机端的标识 ,JS不能实现
1、JS不能修改User-Agent (报错:Refused to set unsafe header “User-Agent”)
前端跨域设置 withCredentials: true
在做登录认证的时候,会出现请求未登录的情况,查看请求头的时候发现并没有把登录时的cookie设置到第二次的请求头里面。查看资料才知道跨域请求要想带上cookie,必须要在ajax请求里加上xhrFields: {withCredentials: true}, crossDomain: true。
再次访问发现请求头可以携带cookie了。
注解@CrossOrigin解决跨域问题
注解@CrossOrigin
出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站。来自EVILL的脚本不能够对你的银行API做出Ajax请求(从你的帐户中取出钱!)使用您的凭据。
跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的策略,如IFRAME或JSONP
一、跨域(CORS)支持:
pring Framework 4.2 GA为CORS提供了第一类支持,使您比通常的基于过滤器的解决方案更容易和更强大地配置它。所以springMVC的版本要在4.2或以上版本才支持@CrossOrigin
二、使用方法:
npm和cnpm
npm (nodejs平台上写的js模块的管理工具 下载、互相依赖等)
/npm install 本地项目的node_modules文件夹 ,
//-g npm config prefix 目录eg: /usr/local
//--save(-S为简写) 保存至package.json dependencies,npm install 会安装 线上会用 {最常用---------}
//--save-dev(-D为简写) 保存至package.json devDependencies,npm install 会安装 仅本地项目使用
//还可以混合使用 npm install lint-staged -D -S 配置
数据请求
1、General
- Request URL: 请求链接
- Request Method: 请求方式(正常表单提交,post)
- Status Code:HTTP响应状态码(状态码有很多)
- Remote Address: 127.0.0.1:8080
- Referrer Policy: no-referrer-when-downgrade
2、Response Headers(响应头)
- access-control-allow-headers: x-requested-with,content-type
- access-control-allow-methods: POST
- access-control-allow-origin: *
- cache-control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
- connection: close(连接的管理)
- content-type: application/json
- date: Fri, 13 Mar 2020 07:40:13 GMT(响应的时间)
- expires: Thu, 19 Nov 1981 08:52:00 GMT
- pragma: no-cache
- server: nginx(HTTP服务器的安装信息)
- set-cookie: XBSERVERSESSID=rjagpa88g4eb46fjt0pp53g6b4; path=/
- transfer-encoding: chunked
- X-Powered-By: Express
3、Request Headers(请求头)
- Accept: application/json, text/plain, / (服务器能发送的媒体类型)
- Accept-Encoding: gzip, deflate, br(支持的编码)
- Accept-Language: zh-CN,zh;q=0.9,en;q=0.8(支持的自然语言)
- Connection: keep-alive(连接状态)
- Content-Length: 30(实体的长度)
- Content-Type:请求的资源响应回来的类型(text/html说明这是个html页面,支持的字符集编码为UTF-8)(实体主体的媒体信息)
- Cookie: 记录用户状态信息(客户端发送给服务器的身份标识)
- Host:主机(指定请求资源的主机)
- Origin: http://localhost:8080
- Referer: http://localhost:8080/(指明了请求当前资源的原始资源的url)
- Sec-Fetch-Mode: cors()
- Sec-Fetch-Site: same-origin
- User-Agent: 浏览器的类型表示,附带你的机器信息(爬虫通过伪造这个,将生硬的爬虫请求变成浏览器请求)
- X-REQUESTED-WITH: XMLHttpRequest
4、Form Data
富文本
const toolbarOptions = [
['bold', 'italic'],
['blockquote'],
[{
'header': 1
}, {
'header': 2
}],
[{
'list': 'ordered'
}, {
'list': 'bullet'
}],
[{
'indent': '-1'
}, {
'indent': '+1'
}],
[{
'header': [1, 2, 3, 4, 5, 6, false]
}],
[{
'color': []
}, {
'background': []
}],
[{
'align': []
}],
// ['clean'],
['link', 'image', {
'video': {
attributes: {
'data-toggle': 'tooltip',
'data-placement': 'bottom',
'title': '上传音视频'
}
}
}],
['clean'],
['undo', 'redo']
// ['html']
];
const titleConfig = {
'ql-bold': '加粗',
'ql-color': '颜色',
'ql-font': '字体',
'ql-code': '插入代码',
'ql-italic': '斜体',
'ql-link': '添加链接',
'ql-background': '背景颜色',
'ql-size': '字体大小',
'ql-strike': '删除线',
'ql-script': '上标/下标',
'ql-underline': '下划线',
'ql-blockquote': '引用',
'ql-header': '标题',
'ql-indent': '缩进',
'ql-list': '列表',
'ql-align': '文本对齐',
'ql-direction': '文本方向',
'ql-code-block': '代码块',
'ql-formula': '公式',
'ql-image': '图片',
'ql-video': '视频',
'ql-clean': '清除字体样式'
}
export function addQuillTitle () {
const oToolBar = document.querySelector('.ql-toolbar'),
aButton = oToolBar.querySelectorAll('button'),
aSelect = oToolBar.querySelectorAll('select')
aButton.forEach(function (item) {
if (item.className === 'ql-script') {
item.value === 'sub' ? item.title = '下标' : item.title = '上标'
} else if (item.className === 'ql-indent') {
item.value === '+1' ? item.title = '向右缩进' : item.title = '向左缩进'
} else {
item.title = titleConfig[item.classList[0]]
}
})
aSelect.forEach(function (item) {
item.parentNode.title = titleConfig[item.classList[0]]
})
}
scss
1 、引用父级选择器“&”
Scss使用"&"关键字在CSS规则中引用父级选择器,例如在嵌套使用伪类选择器的场景下:
/*===== SCSS =====*/
a {
font-weight: bold;
text-decoration: none;
&:hover { text-decoration: underline; }
body.firefox & { font-weight: normal; }
}
/*===== CSS =====*/
a {
font-weight: bold;
text-decoration: none; }
a:hover {
text-decoration: underline; }
body.firefox a {
font-weight: normal; }
无论CSS规则嵌套的深度怎样,关键字"&"都会使用父级选择器级联替换全部其出现的位置:
/*===== SCSS =====*/
#main {
color: black;
a {
font-weight: bold;
&:hover { color: red; }
}
}
/*===== CSS =====*/
#main {
color: black; }
#main a {
font-weight: bold; }
#main a:hover {
color: red; }
"&"必须出现在复合选择器开头的位置,后面再连接自定义的后缀,例如:
/*===== SCSS =====*/
#main {
color: black;
&-sidebar { border: 1px solid; }
}
/*===== CSS =====*/
#main {
color: black; }
#main-sidebar {
border: 1px solid; }
如果在父级选择器不存在的场景使用&,Scss预处理器会报出错误信息。
嵌套属性
CSS许多属性都位于相同的命名空间(例如font-family、font-size、font-weight都位于font命名空间下),Scss当中只需要编写命名空间一次,后续嵌套的子属性都将会位于该命名空间之下,请看下面的代码:
/*===== SCSS =====*/
.demo {
// 命令空间后带有冒号:
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}
/*===== CSS =====*/
.demo {
font-family: fantasy;
font-size: 30em;
font-weight: bold; }
命令空间上可以直接书写CSS简写属性,但是日常开发中建议直接书写CSS简写属性,尽量保持CSS语法的一致性。
.demo {
font: 20px/24px fantasy {
weight: bold;
}
}
.demo {
font: 20px/24px fantasy;
font-weight: bold;
}
transition
/使用transition元素把需要被动画控制的元素,包裹起来
//可以区分不同组织间的动画
JavaScript
1.数组
1.1数组(map、forEach)(超常用)
Array.isArray(obj)//判断是否是数组
1.1.1 map
/map 返回一个新的数组,其本身的数组是不改变的
//map()返回一个新数组,原数组不会改变。
let arr=[1,2,3,4,5];
let list=arr.map(value=>{
return value*value
})
console.log(list)
2、方法二
let data = [1,2,3,4];
let arr = data.map(item => {
return {
label: item,
value: ''
}
})
console.log(arr)
//对象循环
for(let key in objData ){
/key 就为当前的对象里面键(循环出来)
}
1.1.2 forEach
/forEach 可以在以前的基础上进行修改
//forEach()返回值是undefined,不可以链式调用。
//没有办法终止或者跳出forEach()循环,除非抛出异常,所以想执行一个数组是否满足什么条件,返回布尔值,可以用一般的for循环实现,或者用Array.every()或者Array.some();
//$.each()方法规定为每个匹配元素规定运行的函数,可以返回 false 可用于及早停止循环。
let arrData=[1,2,3,4,5];
arrData.forEach((value,index)=>{
return arrData[index]=value*value
})
console.log(arrData) //arr=[1,4,9,16,25];
1、方法一
this.selectList.forEach((item,index)=>{
return this.selectList[index]={
id: item.id,
name: item.name,
number: getId(Math.floor(Math.random() * 10))
}
})
1.2数组(筛选关键字)
filterTreeNode(val){
let arrDara=recurSave(this.newtreeData, val);
function recurSave(list, keyWord) {
let result = [];
if (!list) {
return;
}
for (let index in list) {
let item = list[index];
if (item.title.split(keyWord).length > 1) {
result.push(item);
break;
} else if (item.children && item.children.length > 0) {
result = recurSave(item.children, keyWord);
}
}
return result;
}
this.treeData=arrDara;
console.log(this.treeData)
},
1.3数组(不同类型的递归)
理论:
- 递归就是程序不断调用自身,递归方法就是方法直接或间接调用自身的方法
递归的特点:
- 反复执行的过程(调用自身)
- 结束反复执行过程的条件(方法跳出点)
递归的缺点:
- 耗内存,耗资源,不易阅读!不易阅读!不易阅读!(重要的事情说三遍)
/需求一:已知整个数组可以某个数据的id,如何将其对应的父元素和子元素全部提取出来
function familyTreeqee (arr1, id) {
let temp = []
function forFn(arr, id) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
if (item.value == id) {
temp.push(item);
forFn(arr1, item.parent_id);
break;
} else {
if (item.children) {
forFn(item.children, id);
}
}
}
}
forFn(arr1, id)
return temp
};
/需求二:已知整个数组和id,然后查找当前id的所有数据
function familyTree(arr1,id){
let temp = [];
forFn(arr1, id);
function forFn(arr,id) {
for (let index in arr) {
let item = arr[index];
if (item.value == id) {
temp.push(item);
break;
} else {
if (item.children) {
forFn(item.children, id)
}
}
}
}
return temp[0];
};
/需要三: 对树进行循环,然后找出不同的children,给key存在的添加属性
function recurSive(data, key) {
let result = {};
if (!data) {
return;
}
for (let index in data) {
let item = data[index];
if (item.key == key) {
result = item.disabled = true;
break;
} else if (item.children && item.children.length > 0) {
result = recurSive(item.children, key);
}
}
return result;
}
/需求四:对比两个数组,将重复的提取出来,合并数组
oldTree = oldTree.concat(data);
unique(arr) {
for (let i = 0, len = arr.length; i < len; i++) {
for (let j = i + 1, len = arr.length; j < len; j++) {
if (arr[i].id === arr[j].id) {
arr.splice(j, 1);
j--;
len--;
}
}
}
return arr;
},
/需求五:两个数组相比,key值相同就合并 (降序)
let showArr = this.cardTable.tableData.map(item => {
return item.id;
});
data.forEach(item => {
let sortId = showArr.indexOf(item.id);
item.sortId = sortId;
});
bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j].sortId > arr[j + 1].sortId) {
let tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
return arr;
},
/需求六:单个数组去重
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
/改变数组里面的某些值
function a(){}
/根据一个数组,给两个id值,判断它们的层级关系 (功能未实现,因为是异步树,不知道所有的树结构)
//1、先找出oldId的树结构,再找出newId的树结构
function a(){}
/实时刷新树的状态
ces() {
let arr = this.getDeviceId(this.treeData);
if (arr && arr.length) {
gridFacilityOnline
.post({ id: arr.toString(), noloading: true })
.then(res => {
if (res && res.ok) {
this.intoArrayTree(res.list);
} else {
this.$message.error(res.error);
}
});
}
this.timer = setTimeout(() => {
this.ces();
}, 10 * 1000);
},
//将数组遍历又重新弄、
initArr(data) {
function transferKey(arr) {
for (let index in arr) {
if (arr[index].is_new_online) {
arr[index].selectable = true;
arr[index].slots = {
icon: "video-camera"
};
} else if (arr[index].is_new_online == 0) {
arr[index].selectable = false;
arr[index].slots = {
icon: "camera"
};
}
if (arr[index].children instanceof Array) {
transferKey(arr[index].children);
}
}
return arr;
}
return transferKey(data);
},
//根据id已经查到的状态将它一层一层的再包裹进树里面
intoArrayTree(list) {
this.newTreeDasta = JSON.parse(JSON.stringify(this.treeData));
for (let key in list) {
// eslint-disable-next-line no-inner-declarations
function transferKey(arr) {
arr.forEach(obj => {
if (obj.id === key) {
obj.is_new_online = list[key];
// obj.is_new_online = 1;
}
if (obj.children instanceof Array) {
transferKey(obj.children);
}
});
return arr;
}
this.newTreeDasta = transferKey(this.newTreeDasta);
}
this.treeData = JSON.parse(JSON.stringify(this.newTreeDasta));
this.treeData = this.initArr(this.treeData);
},
//循环数组,根据特有的属性将是设备的id弄出来
getDeviceId(data) {
if (!(data && data.length)) return;
let deviceId = [];
function setName(data) {
//遍历树 获取id数组
for (let i in data) {
if (data[i].is_facility == 1) {
deviceId.push(data[i].id);
}
if (data[i].children) {
setName(data[i].children);
}
}
}
setName(data);
return deviceId;
},
//反面显示
setPreviewValue(defaultValue) {
if (isArray(defaultValue)) {
this.modelData = mapThisData(defaultValue, item => {
return {
label: item.name,
value: `${item.id}-${item.name}`
};
});
} else if (isObject(defaultValue)) {
this.modelData = {
label: defaultValue.name,
value: `${defaultValue.id}-${defaultValue.name}`
};
}
console.log(this.modelData);
},
1.6数组(数组的基本方法)
concat() var a = [1,2,3]; a.concat(4,5) 1,2,3,4,5连接两个或更多的数组,并返回结果。
``join()`` arr.join(",") 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
pop() //删除并返回数组的最后一个元素
pop() 方法将删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值。
如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。
push() //向数组的末尾添加一个或更多元素,并返回新的长度。
reverse() arr.reverse() George,John,Thomas Thomas,John,George 颠倒数组中元素的顺序。
shift() //删除并返回数组的第一个元素
slice() //从某个已有的数组返回选定的元素
arrayObject.slice(start,end) start必需。规定从何处开始选取。 可选。规定从何处结束选取。
sort() //对数组的元素进行排序(按何种情况待定)
``splice() `` //arrayObject.splice(index,howmany,item1,.....,itemX) 删除元素,并向数组添加新元素。
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX 可选。向数组添加的新项目。
toSource() //返回该对象的源代码。
``toString()`` //把数组转换为字符串,并返回结果。
arrayObject 的字符串表示。返回值与没有参数的 join() 方法返回的字符串相同。
toLocaleString() //把数组转换为本地数组,并返回结果。
unshift()// arrayObject.unshift(newelement1,....)向数组的开头添加一个或更多元素,并返回新的长度。
newelement1 必需。向数组添加的第一个元素。
newelement2 可选。向数组添加的第二个元素。
newelementX 可选。可添加若干个元素。
valueOf() //返回数组对象的原始值
1.7数组合并
let arr3 = arr1.concat(arr2);/arr1不能够为空
2.JSON数组、字符串、对象之间的转换(超常用)
1、将JSON数组转为正常的数组(对请求的数据,进行解析)
/JSON.parse(data.place_pics)
JSON.parse()第二个参数
JSON.parse()第二个参数为选填,用于将转换后的每个成员变量都调用此函数。
- 以下为权限树json的转化代码
- 每个被转化的对象属性都会被遍历
- 被遍历的每一项都是一个js对象
- eg. 一个含有两个属性值得对象会被遍历三次
- eg. {name: "分析结果", checked: false, id: 32}
2、将JavaScript对象转化为JSON数据(字符串)(将数据传入后端,进行数据保存)
/ 将JavaScript对象转化为JSON数据(字符串) JSON.stringify(value,replacer)
JSON.stringify(value,replacer)第二个参数
可选。用于转换结果的函数或数组。
- 如果 replacer 为函数,则 JSON.stringify 将调用该函数。
- 函数的返回值将作为转化结果
- 如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。
3.vue-dialog的弹出层组件
this.$Dialog({
content: () =>
import("@/components/publicity-release.vue"),
dialogProps: {
width: "80%",
title: "发布信息",
bodyStyle: this.acceptModelBodyStyle,
modalStyle: this.modalStyle,
mask: true, //这两个,必须一起使用 ,让弹窗,有模糊层
maskStyle: { backgroundColor: "rgba(0,0,0,0.25)" },
footer: null
},
contentProps: {
whichOne: {
whichTitle: "请输入标题",
whichAbstract: "请输入摘要",
whichImg: "图片",
whichConcent: "请填写内容",
whichTitleData: "xxxx预览",
type: 3
}
}
});
4.Object.keys(obj)
5. this.$router.push
router.go(n):接受一个整数作为参数,类似window.history.go(n),在浏览器历史记录中前进或后退几步
router.push( location ):跳转导航的方法,这种方法会向history栈添加一个新的记录
router.replace( location ):和router.push()类似,但是它会替换掉当前的history记录,不会添加新的记录
router.back():相当于router.go(-1)
router.forward():相当于router.go(1)
router.resolve(location):解析目标路由,接受一个地址参数,返回location,route,href等属性信息,还可以接受当前默认路由current和当前路由上附加路径append 两个参数
router.onReady(callback,[errorCallback]){}:把一个回调排队,在路由完成初始导航时调用。
router.onError(callback):注册一个回调,该回调会在路由导航过程中出错的时候被调用,但是对被调用的错误情形有要求:
1、错误在一个路由守卫函数中被同步抛出
2、错误在一个路由守卫函数中通过调用next(error)的方式异步捕获并处理
3、渲染一个路由的过程中,需要尝试解析一个异步组件时发生错误
$route.fullPath :完成解析后的url,包含查询参数和hash的完整路径
$route.path:路径,字符串类型,解析为绝对路径
$route.hash: 当前路由的hash值(带#号的),如果没有hash值则为空字符串
$route.name:当前路由的名称,如果有的话(用于命名路由)
$route.params:一个键值对对象,路由参数
$route.query:一个键值对对象,表示url查询参数
$route.matched:一个包含了当前路由的所有嵌套路径片段的路由记录(routes配置数组中的对象副本)
$route.redirectedFrom:重定向来源的路由的名字,如果存在重定向的话。
//1、路由跳转(且路由上面带着值,显示出来)
this.$router.push({
path: "cases-list",
query: {}
});
/取值
let href = decodeURIComponent(window.location.href);
let index = href.indexOf("?");//接受参数
//2、路由跳转(路由上面没有值)
this.$router.push({name: 'dispatch', params: {paicheNo: obj.paicheNo}})
// 在被跳转的页面使用
this.$route.params //接受参数
6.String (字符串和数字的变化)
//js字符串转换数字。方法主要有三种
/ 转换函数、强制类型转换、利用js变量弱类型转换。
1. 转换函数:
js提供了parseInt()和parseFloat()两个转换函数。前者把值转换成整数,后者把值转换成浮点数。只有对String类型调用这些方法,这两个函数才能正确运行;对其他类型返回的都是NaN(Not a Number)。
一些示例如下:
parseInt("1234blue"); //returns 1234
parseInt("0xA"); //returns 10
parseInt("22.5"); //returns 22
parseInt("blue"); //returns NaN
parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。基是由parseInt()方法的第二个参数指定的,示例如下:
parseInt("AF", 16); //returns 175
parseInt("10", 2); //returns 2
parseInt("10", 8); //returns 8
parseInt("10", 10); //returns 10
如果十进制数包含前导0,那么最好采用基数10,这样才不会意外地得到八进制的值。例如:
parseInt("010"); //returns 8
parseInt("010", 8); //returns 8
parseInt("010", 10); //returns 10
parseFloat()方法与parseInt()方法的处理方式相似。
使用parseFloat()方法的另一不同之处在于,字符串必须以十进制形式表示浮点数,parseFloat()没有基模式。
下面是使用parseFloat()方法的示例:
parseFloat("1234blue"); //returns 1234.0
parseFloat("0xA"); //returns NaN
parseFloat("22.5"); //returns 22.5
parseFloat("22.34.5"); //returns 22.34
parseFloat("0908"); //returns 908
parseFloat("blue"); //returns NaN
2. 强制类型转换
还可使用强制类型转换(type casting)处理转换值的类型。使用强制类型转换可以访问特定的值,即使它是另一种类型的。
ECMAScript中可用的3种强制类型转换如下:
//Boolean(value)——把给定的值转换成Boolean型;
//Number(value)——把给定的值转换成数字(可以是整数或浮点数);
//String(value)——把给定的值转换成字符串。
用这三个函数之一转换值,将创建一个新值,存放由原始值直接转换成的值。这会造成意想不到的后果。
当要转换的值是至少有一个字符的字符串、非0数字或对象时,Boolean()函数将返回true。如果该值是空字符串、数字0、undefined或null,它将返回false。
可以用下面的代码段测试Boolean型的强制类型转换。
Boolean(""); //false – empty string
Boolean("hi"); //true – non-empty string
Boolean(100); //true – non-zero number
Boolean(null); //false - null
Boolean(0); //false - zero
Boolean(new Object()); //true – object
Number()的强制类型转换与parseInt()和parseFloat()方法的处理方式相似,只是它转换的是整个值,而不是部分值。示例如下:
Number(false) 0
Number(true) 1
Number(undefined) NaN
Number(null) 0
Number( "5.5 ") 5.5
Number( "56 ") 56
Number( "5.6.7 ") NaN
Number(new Object()) NaN
Number(100) 100
最后一种强制类型转换方法String()是最简单的,示例如下:
var s1 = String(null); //"null"
var oNull = null;
var s2 = oNull.toString(); //won't work, causes an error
3. 利用js变量弱类型转换
<script>
var str= '012.345 ';
var x = str-0;
x = x*1;
</script>
上例利用了js的弱类型的特点,只进行了算术运算,实现了字符串到数字的类型转换,不过这个方法还是不推荐的
7. this.$refs
/ ref 有三种用法: 主要是vue里面的获取dom元素的方法
// 1、ref 加在普通的元素上,用this.ref.name 获取到的是dom元素
// 2、ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法。
// 3、如何利用 v-for 和 ref 获取一组数组或者dom 节点
this.$refs["childonemyyy"].isAdd;//"mychildone"用在父组件上引用子组件值,返回子组件上的data数据
this.$refs["myspan"].className //redmy 用在元素上,返回元素节点对象
ref 有三种用法:
1、ref 加在普通的元素上,用this.ref.name 获取到的是dom元素
2、ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法。
3、如何利用 v-for 和 ref 获取一组数组或者dom 节点
this.$refs.text.value = 'Hello Vue!'
this.$refs.canvas
this.$refs["canvas"]
注意:
1、ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
2、如果ref 是循环出来的,有多个重名,那么ref的值会是一个数组 ,此时要拿到单个的ref 只需要循环就可以了。
8.捕获和冒泡
原理 :
DOM事件流(event flow ):
存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
事件冒泡(event capturing) :
当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window 。(注意这里传递的仅仅是事件 并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。)
事件捕获(event capturing):
当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。
事件捕获和事件冒泡
DOM事件流
document->html->body->div(最末)->body->html->document
(捕获) (冒泡)
当我们点击子元素,触发的时事件会传递给父元素,这就是事件冒泡。这时候为了不让父元素受影响就要阻止冒泡
//事件冒泡会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。
event.stopPropagation();/阻止事件冒泡
//stopPropagation也是事件对象(Event)的一个方法,作用是阻止目标元素的冒泡事件,但是会不阻止默认行为。
总结使用方法
//当需要停止冒泡行为时,可以使用
function stopBubble(e) {
//如果提供了事件对象,则这是一个非IE浏览器
if ( e && e.stopPropagation ) {
//因此它支持W3C的stopPropagation()方法
e.stopPropagation(); }
else {
//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
/当需要阻止默认行为时,可以使用
//阻止浏览器的默认行为
function stopDefault( e ) {
//阻止默认浏览器动作(W3C)
if ( e && e.preventDefault )
{ e.preventDefault(); }
//IE中阻止函数器默认动作的方式
else {
window.event.returnValue = false;
return false; }
}
事件注意点
event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等;
event对象只在事件发生的过程中才有效。
/firefox里的event跟IE里的不同,IE里的是全局变量,随时可用;firefox里的要用参数引导才能用,是运行时的临时变量。
//事件捕获会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。
event.stopImmediatePropagation();
9. …item
//这个就是让对应的数据,一样展开,主要在对象这边有效
let data={
...data,
name:"蛋烘糕",
}
10.beforeRouteEnter()和beforeRouteLeave()
beforeRouteEnter(to, from, next) {
let str = to.fullPath.split("?")[0];
let modulePath = str.replace(/^\//, "").split("/")[0];
let param = assignment[modulePath].nav;
if (param && param != "") {
generalNav.post({ module_name: param }).then(res => {
if (res && res.list && res.list.length) {
for (let index in res.list) {
if (res.list[index].img) {
let url = assignment[modulePath].icon + res.list[index].img;
res.list[index].img = require("@/assets" + url + "");
}
}
totalMenus = res.list;
next();
} else {
$Msg.error(res.error || "无权限查看!");
window.history.back();
}
});
}
},
vue 导航守卫
1、beforeRouteEnter(to, from, next){
要执行的代码操作
next();
}
beforeRouteEnter:进入路由之前执行的函数。
next(); --》 必须有这个,相当于一个按钮开启一样。
2、beforeRouteLeave(to, from, next){
要执行的代码操作
next();
}
beforeRouteLeave:离开路由之前执行的函数。
next(); --》 必须有这个,相当于一个按钮开启一样。
11.Promise
对象(不常用)
遇见情况:当语音在播放的时候,会遇见,不能够播放的时候。这个问题,没有找到合适的解决方法。
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成Promise');
resolve('要返回的数据可以任何数据例如接口返回数据');
}, 2000);
});
12.watch(主要是需要深度监控)
//handle就是你watch中需要具体执行的方法
// deep:就是你需要监听的数据的深度,一般用来监听对象中某个属性的变化
//immediate:在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调:
//watch 里面还有一个属性 deep,默认值是 false,代表是否深度监听
watch: {
fileList: {
deep: true,
handler: function(val) {
this.$store.commit("updateDataStore", { key: this._name, value: val });
}
}
},
watch: {
firstName: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
immediate: true
}
}
watch: {
eventStatistics: {
handler(n, o) {
console.log(n, o);
this.$nextTick(() => {
this.changeEchart();
});
},
immediate: true, //刷新加载 立马触发一次handler
deep: true // 可以深度检测到 person 对象的属性值的变化
}
},
13.this.$emit(子组件传值给父组件)
vm.$emit( event, arg )
$emit 绑定一个自定义事件event,当这个这个语句被执行到的时候,就会将参数arg传递给父组件,父组件通过@event监听并接收参数
//父组件主要使用事件 @“定义的事件”=“定义的事件”,获取子组件穿过来的值
//子组件传值父组件,是通过props传递过来的
14.this.$nextTick()(主要在dom更新之后执行)
/vue实现响应式并不是数据发生变化后dom立即变化,而是按照一定的策略来进行dom更新。
//$nextTick是在下一次dom更新循环结束之后执行延迟回调,在修改数据之后使用这个方法,立即更新dom
//改变数据
this.message = 'changed'
//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(this.$el.textContent) // 并不会得到'changed'
//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
console.log(this.$el.textContent) //可以得到'changed'
})
15.this.$forceUpdate()
this.$forceUpdate() 强制刷新(因为数据层次太多,render函数没有自动更新,需手动强制刷新。)
16.正则表达式
17.sessionStorage存储
存:
sessionStorage.setItem("signoutShow",this.signoutShow);
取:
this.signoutShow = sessionStorage.getItem('signoutShow');
/vue中由于页面刷新会导致有些参数被清空,用sessionStorage存储数据一般是在 beforeMount() 挂载前取, beforeUpdate() 更新渲染时存:
beforeMount() {
this.signoutShow = sessionStorage.getItem('signoutShow');
},
beforeUpdate() {
sessionStorage.setItem("signoutShow",this.signoutShow);
}
//【注】sessionStorage 存储数据一般为对象,当存储值为 true/false 时,会发现存取都可以得到正确的值,但是页面渲染的结果不对,这是因为存储的类型为字符串类型,而 true/false 为bool 类型,
// sessionStorage.getItem('signoutShow'); 取值的时候会得到带引号的值,比如说"true" / "false" ,那么这时候就需要用 JSON.parse 转换一下类型就好了
beforeMount() {
var signoutBool = sessionStorage.getItem('signoutShow');
this.signoutShow = JSON.parse(signoutBool);
}
18.var 和let
题目1:(晕得很------------)
var a = 99; // 全局变量a
f(); // f是函数,虽然定义在调用的后面,但是函数声明会提升到作用域的顶部。
console.log(a); // a=>99, 此时是全局变量的a
function f() {
console.log(a); // 当前的a变量是下面变量a声明提升后,默认值undefined
var a = 10;
console.log(a); // a => 10
}
// 输出结果:
undefined
10
99
19.vue store存储commit 和dispatch(vuex)
this.$store.commit('toShowLoginDialog', true);
this.$store.dispatch('toShowLoginDialog',false)
主要区别是:
dispatch:含有异步操作,例如向后台提交数据,写法: this.$store.dispatch('mutations方法名',值)
commit:同步操作,写法:this.$store.commit('mutations方法名',值)
20.识别屏幕宽高
//js中制作滚动代码的常用属性
页可见区域宽: document.body.clientWidth;
网页可见区域高: document.body.clientHeight;
网页可见区域宽: document.body.offsetWidth (包括边线的宽);
网页可见区域高: document.body.offsetHeight (包括边线的宽);
网页正文全文宽: document.body.scrollWidth;
网页正文全文高: document.body.scrollHeight;
网页被卷去的高: document.body.scrollTop;
网页被卷去的左: document.body.scrollLeft;
网页正文部分上: window.screenTop;
网页正文部分左: window.screenLeft;
屏幕分辨率的高: window.screen.height;
屏幕分辨率的宽: window.screen.width;
屏幕可用工作区高度: window.screen. availHeight;
21.v-if和v-show的区别
//相同点
两者都是在判断DOM节点是否要显示
//不同点
`1、实现方式`
v-if是根据后面数据的真假值判断直接从Dom树上删除或重建元素节点
v-show只是在修改元素的css样式,也就是display的属性值,元素始终在Dom树上。
`2、编译过程`
v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
v-show只是简单的基于css切换;
`3、编译条件`
v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译;
v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素始终被保留;
`4、性能消耗`
v-if有更高的切换消耗,不适合做频繁的切换;
v-show有更高的初始渲染消耗,适合做频繁的额切换;
22.mixins
Mixins是一种分发Vue组件中可复用功能的非常灵活的一种方式。
什么时候使用`Mixins
页面的风格不用,但是执行的方法和需要的数据类似,我们是选择每个都写呢还是提取出公共部分呢?
// modal.vue
// 将mixin引入该组件,就可以直接使用 toggleShow() 了
import {mixin} from '../mixin.js'
export default {
mixins: [mixin],
mounted () {
}
}
// tooltip组件同上
23、vBus(是一旦触发,另外一边就有数据)
//传值 ,现在看来主要用于关闭弹窗,更新列表数据,传递值
vBus.$emit("reGetBubble",data);
//接收值
vBus.$off("reGetBubble").$on("reGetBubble", () => {
});
24、!!或者
!!一般用来将后面的表达式转换为布尔型的数据(boolean) 因为javascript是弱类型的语言(变量没有固定的数据类型)所以有时需要强制转换为相应的类型,类似的如: a=parseInt(“1234”) a=“1234”+0 //转换为数字 b=1234+“” //转换为字符串 c=someObject.toString() //将对象转换为字符串 其中第1种、第4种为显式转换,2、3为隐式转换 布尔型的转换,javascript约定和c类似,规则为 ·false、undefinded、null、0、“” 为 false ·true、1、“somestring”、[Object] 为 true
/在js中看源码时有时候出现两个!!,我起初以为是js的其他语法,其实!!就是两次取“非”的运算。
alert(null);//false
alert(!null);//true
alert(!!null);//false
25、深拷贝和浅拷贝
- 浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用
- 深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”**
我的回答是:浅拷贝是拷贝了对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是另外申请了一块内存,内容和原对象一样,更改原对象,拷贝对象不会发生变化;
但是面试官给我说:浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;
其实总结来看,浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。
实现浅拷贝的第一种方法
/**
* Created by 811 on 2018/7/27.
*/
function simpleClone(initalObj) {
var obj = {};
for ( var i in initalObj) {
obj[i] = initalObj[i];
}
return obj;
}
var obj = {
a: "hello",
b:{
a: "world",
b: 21
},
c:["Bob", "Tom", "Jenny"],
d:function() {
alert("hello world");
}
};
var cloneObj = simpleClone(obj);
console.log(cloneObj.a);
console.log(cloneObj.b);
console.log(cloneObj.c);
console.log(cloneObj.d);
//更改原对象中的a,b,c,d,看看拷贝过来的对象是否变化
cloneObj.a = "changed";
cloneObj.b.a = "changed";
cloneObj.b.b = 25;
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
console.log(obj.a); //hello
console.log(obj.b); //{a:"changed",b:25},事实上就是只有对象是拷贝的引用类型
console.log(obj.c); //['Bob','Tom','Jenny']
console.log(obj.d); //...alert("hello world")
事实证明面试官说的是对的,浅拷贝就是拷贝了一层,除了对象是拷贝的引用类型,其他都是直接将值传递,有自己的内存空间的
实现浅拷贝的第二种方法
ES6中的Object.assign方法,Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, …sources)
参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。
var obj1 = {
a: "hello",
b: {
a: "hello",
b: 21}
};
var cloneObj1= Object.assign({}, obj1);
cloneObj1.a = "changed";
cloneObj1.b.a = "changed";
console.log(obj1.a); //hello
console.log(obj.b.a); // "changed"
另外,(这里其实没太看懂),为什么上面的改了,这里却没有改。。。。说是可以拷贝深一层,如果对象只有一层的话,可以使用这个函数作为深拷贝的方法
var obj2 = { a: 10, b: 20, c: 30 };
var cloneObj2 = Object.assign({}, obj2);
cloneObj2.b = 100;
console.log(obj2);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(cloneObj2);
// { a: 10, b: 100, c: 30 }
深拷贝的实现方式
1、手动复制
把一个对象的属性复制给另一个对象的属性
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
但这样很麻烦,要一个一个自己复制;而且这样的本质也不能算是 Deep Copy,因为对象里面也可能回事对象,如像下面这个状况:
var obj1 = { body: { a: 10 } };
var obj2 = { body: obj1.body };
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 20 } } <-- 被改到了
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// true
虽然obj1跟obj2是不同对象,但他们会共享同一个obj1.body,所以修改obj2.body.a时也会修改到旧的。
2、对象只有一层的话可以使用上面的:Object.assign()函数
Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。
因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。
3、转成 JSON 再转回来
用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
这样做是真正的Deep Copy,这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。
var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 没复制
要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。
4、递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
if (typeof initalObj[i] === 'object') {
obj[i] = (initalObj[i].constructor === Array) ? [] : {};
arguments.callee(initalObj[i], obj[i]);
} else {
obj[i] = initalObj[i];
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。
为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。
改进版代码如下:
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
5、使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
26、富文本
富文本里面的图片问题,如何提取里面的图片,让字段不显得那么长(难点呀,还没有百度到,难受)
<div class="tinymce">
<vue-editor
ref="content"
v-model="formData.content"
@focus="onEditorFocus($event)"
@blur="onEditorBlur"
useCustomImageHandler
@selection-change="onSelectionChange"
@image-added="handleImageAdded"
/>
</div>
import { VueEditor } from "vue2-editor/dist/vue2-editor.core.js";
import axios from "axios";
<style scoped>
@import "~vue2-editor/dist/vue2-editor.css";
/* Import the Quill styles you want */
@import "~quill/dist/quill.core.css";
@import "~quill/dist/quill.bubble.css";
@import "~quill/dist/quill.snow.css";
.button_bull {
margin-left: 20px;
}
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>
method:{
onEditorFocus(val) {
// 富文本获得焦点时的事件
val.enable(false); // 在获取焦点的时候禁用
},
handleImageAdded(file, Editor, cursorLocation, resetUploader) {
let formData = new FormData();
formData.append("image", file);
console.log(formData, file);
upfileIndex.post(formData).then(res => {
console.log(res);
});
axios({
url: this.picUrl,
method: "POST",
data: formData
})
.then(result => {
console.log(result);
let url = result.data.url; // Get url from response
Editor.insertEmbed(cursorLocation, "image", url);
resetUploader();
})
.catch(err => {
console.log(err);
});
},
}
/哪种时候的需要传token的真的难受,为啥不能上传成功?真的是头疼 !!!!!!!!
27、浏览器指纹
28、全局设置移入,移出,样式
/什么时候触发这个函数,问题在这里
mounted() {
this.closeOtherPopup();
},
methods: {
closeOtherPopup() {
document.onclick = function(event) {
let el = event ? event.target : window.event.srcElement;
switchDisplay.map(item => {
let targetArea = document.getElementById(item.totalTitle);
let targetArea1 = document.getElementById(item.popupTitle);
if (!targetArea.contains(el)) {//滑出指定的div,其显示问题/··这里就是重点啊··
targetArea1.style.display = "none";
}
});
};
},
changeInformation() {
this.status = 3;
if (this.infomation) {
let targetArea1 = document.getElementById("massages-id");
targetArea1.style.display == "none"
? (targetArea1.style.display = "block")
: (targetArea1.style.display = "none");
} else {
this.infomation = !this.infomation;
}
},
}
29、js轮询
js轮询还是下面的这种方法有比较大的问题,所以还是需要自己循环自己,这种比较有效果,当页面销毁时记得一定要清除定时器,不然页面多次切换时,容易造成多次请求
范例:(循环自己,记得销毁清除)
getDataAsync(){
this.timer = setTimeout(() => {
this.getDataAsync();
}, 10 * 1000);
}
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer); //关闭
}
},
不好的范例:
js setInterval每隔一段时间执行一次
js setInterval每隔一段时间执行一次
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
function funcDemo(){
//每隔3秒执行一次countTime方法
window.setInterval("countTime()",3000);
}
window.onload = funcDemo;
另外有setTimeout方法,这两个方法之间的区别就是:
setInterval方法是每隔一段时间执行一次,是循环执行的,
而setTimeout方法是页面加载完毕之后的规定时间内执行一次,就不再执行了。
done!
30、常见的HTTP状态码
100 客户端可以继续
101 指示服务器正根据 Upgrade 头切换协议
200 请求正常成功
201 指示请求成功并在服务器上创建了一个新资源
202 指示已接受请求进行处理但处理尚未完成
203 指示客户端呈现的元信息并不源自服务器
204 指示请求成功但没有返回新信息
205 指示代理应该 重置导致请求被发送的文档视图
206 指示服务器已完成对资源的部分 GET 请求
300 请求的资源对应于表示形式集合中的某种表示形式,每种表示形式都有自己的特定位置
301 指示已经将资源永久地移动到了某个新位置,并且将来的引用应将新 URI 用于其请求
302 指示已经将资源暂时地移动到了另一个位置,但将来的引用仍应使用原来的 URI 访问该资源。 保留此定义是为了向后兼容。SC_FOUND 现在是首选定义
303 指示可在另一个 URI 之下找到该请求的响应
304 指示条件 GET 操作发现资源可用但不可修改
305 指示必须 通过 Location 字段给定的代理访问请求资源
307 指示请求的资源暂时驻留在另一个 URI 之下。临时 URI 应该 通过响应中的 Location 字段提供
400 指示客户端发送的请求在语法上不正确
401 指示请求需要进行 HTTP 验证
402 保留此代码以备将来使用
403 指示服务器理解请求但拒绝完成它
404 指示请求的资源不可用
405 指示 Request-Line 中指定的方法不支持 Request-URI 标识的资源
406 指示请求标识的资源只能生成响应实体,根据请求中发送的 accept 头,这些响应实体具有不可接受的内容特征
407 指示客户端必须 首先通过代理验证其自身
408 指示客户端没有在服务器准备等待的时间内生成请求
409 指示由于与当前资源状态冲突请求无法完成
410 指示资源在服务器上不再可用并且不知道转发地址。应该 认为此条件是永久性的
411 指示在没有定义 Content-Length 的情况下无法处理请求
412 指示在服务器上测试一个或多个请求头字段中给出的前提时,该前提被求值为 false
413 指示因为请求实体大于服务器愿意或能够处理的实体,所以服务器拒绝处理请求
414 指示因为 Request-URI 的长度大于服务器愿意解释的 Request-URI 长度,所以服务器拒绝为请求提供服务
415 指示因为请求实体的格式不受请求方法的请求资源支持,所以服务器拒绝为请求提供服务
416 指示服务器无法服务于请求的字节范围
417 指示服务器无法服务于请求的字节范围
500 指示 HTTP 服务器内存在错误使服务器无法完成请求
501 指示 HTTP 服务器不支持完成请求所需的功能
502 指示 HTTP 服务器在充当代理或网关时从它参考的服务器接收到一个无效响应
503 指示 HTTP 服务器暂时过载,并且无法处理请求
504 指示服务器在充当网关或代理时没有从上游服务器接收到及时的响应
505 指示服务器不支持或拒绝支持请求消息中使用的 HTTP 协议版本
31、window.location.href(对路由的处理尤为重要)
javascript中的location.href有很多种用法,主要如下。
self.location.href="/url" 当前页面打开URL页面
location.href="/url" 当前页面打开URL页面
windows.location.href="/url" 当前页面打开URL页面,前面三个用法相同。
this.location.href="/url" 当前页面打开URL页面
parent.location.href="/url" 在父页面打开新页面
top.location.href="/url" 在顶层页面打开新页面
如果页面中自定义了frame,那么可将parent self top换为自定义frame的名称,效果是在frame窗口打开url地址
此外,window.location.href=window.location.href;和window.location.Reload()和都是刷新当前页面。区别在于是否有提交数据。当有提交数据时,window.location.Reload()会提示是否提交,window.location.href=window.location.href;则是向指定的url提交数据
history.back(-1):直接返回当前页的上一页,数据全部消息,是个新页面
history.go(-1):也是返回当前页的上一页,不过表单里的数据全部还在
history.back(1) 前进
history.back(-1) 后退
window.location.reload(); //刷新
window.history.go(1); //前进
window.history.go(-1); //返回+刷新
window.history.forward(); //前进
window.history.back(); //返回
/ window.parent.parent.location.href=“你定义要跳转的页面”
function(){}
/document.referrer
let OldHref = document.referrer;/对于当前window的前一个页面,window.parent.HistoryBack();将事件写在父级上面,然后代替子集页面写函数,绑在父级的window上面
window.location.href = OldHref.replace("&is_command=1", "");
32、dom任一位置插入节点
33、100%和100vh的区别
h就是当前屏幕可见高度的1%,也就是说
height:100vh == height:100%;
但是当元素没有内容时候,设置height:100%,该元素不会被撑开,此时高度为0,
但是设置height:100vh,该元素会被撑开屏幕高度一致。
34、px、em、rem、%、vw、vh、vm这些单位的区别
-
px
/px就是pixel的缩写,意为像素。px就是设备或者图片最小的一个点,比如常常听到的电脑像素是1024x768的,表示的是水平方向是1024个像素点,垂直方向是768个像素点。 //是我们网页设计常用的单位,也是基本单位。通过px可以设置固定的布局或者元素大小,缺点是没有弹性。 //特点是1. em的值并不是固定的; 2. em会继承父级元素的字体大小。
-
em
/参考物是父元素的font-size,具有继承的特点。如果自身定义了font-size按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值。 //特点是1. em的值并不是固定的; 2. em会继承父级元素的字体大小。
-
rem
/em是相对于根元素html,这样就意味着,我们只需要在根元素确定一个参考值,可以设计HTML为大小为10px,到时设置1.2rem就是12px.以此类推。 //优点是,只需要设置根目录的大小就可以把整个页面的成比例的调好。
-
%
/一般来说就是相对于父元素的, //1、对于普通定位元素就是我们理解的父元素 //2、对于position: absolute;的元素是相对于已定位的父元素 //3、对于position: fixed;的元素是相对于ViewPort(可视窗口),
-
vw
/css3新单位,view width的简写,是指可视窗口的宽度。假如宽度是1200px的话。那10vw就是120px //举个例子:浏览器宽度1200px, 1 vw = 1200px/100 = 12 px
-
vh
/css3新单位,view height的简写,是指可视窗口的高度。假如高度是1200px的话。那10vh就是120px //举个例子:浏览器高度900px, 1 vh = 900px/100 = 9 px。
-
vm
/css3新单位,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm 举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度,1 vm = 900px/100 = 9 px。
//兼容性太差 ,现在基本上没人用,我试了一下Chrome就用不了。
## 35、iframe
### 1、行内元素(内联元素)
### 2、div高度大于iframe高度5px左右
**问题**:用一个div包裹一个iframe,但无论如何,div的高度都比iframe高了5px。
~~~html
<div id="iframeContent">
<iframe id="iframe" name="iframe" src="home.html" style="width:100%;" frameborder="0" scrolling="no"
"iframeHeight();"></iframe>
</div>
~~~
**问题原因**:使用了HTML5的头部,在使用如下两个头部声明时会使iframe不能填满div,从而产生5px的高度差。
~~~js
<!DOCTYPE html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
~~~
**解决方案**:在为iframe添加属性:**vertical-align:bottom**;或者**display:block;**
其中vertical-align:bottom;表示把元素的顶端与行中最低的元素的顶端对齐。即是以元素底线作为与父元素垂直对齐的基线。
display:block;表示将元素转换为块级元素。iframe本身是一个内联框架,他本身是从div中分出了一部分空间,作为自己的空间,而将其转换为块级对象时,他就会自己从新的一行开始显示内容,而不依赖于div。
**内联对象和块级对象的区别**:
内联对象:不会前后换行,不会从新的一行开始,而是从包裹它的内容一直往后走。
块级对象:前后换行,从新的一行开始往下走,不依赖于包裹他的对象。
## 36、javascript -- 判断是否为某个数据类型
场景:对于后端的返回的数据类型进行严格判断,避免出现不必要的问题,返回的类型可能不是唯一的,不需要判断不是什么,应该判断是什么
/判断是否为数组
isArray = function (source) {
return '[object Array]' == Object.prototype.toString.call(source);
};
/判断是否为日期对象
isDate = function(o) {
// return o instanceof Date;
return {}.toString.call(o) === "[object Date]" && o.toString() !== 'Invalid Date' && !isNaN(o);
};
/判断是否为Element对象
isElement = function (source) {
return !!(source && source.nodeName && source.nodeType == 1);
};
/判断目标参数是否为function或Function实例
isFunction = function (source) {
// chrome下,'function' == typeof /a/ 为true.
return '[object Function]' == Object.prototype.toString.call(source);
};
/判断目标参数是否number类型或Number对象
isNumber = function (source) {
return '[object Number]' == Object.prototype.toString.call(source) && isFinite(source);
};
/ 判断目标参数是否为Object对象
isObject = function (source) {
return 'function' == typeof source || !!(source && 'object' == typeof source);
};
/判断目标参数是否string类型或String对象
isString = function (source) {
return '[object String]' == Object.prototype.toString.call(source);
};
/判断目标参数是否Boolean对象
isBoolean = function(o) {
return typeof o === 'boolean';
};