前端重点知识点
- 执行上下文创建阶段:
- this也属于变量对象中的一员
- 上下文分为全局执行上下文和函数执行上下文
- new关键词的作用:
- 什么是event loop:
- 深拷贝和浅拷贝:
- 闭包三个特性
- jq核心代码
- 隐式类型转化
- 获得时间戳
- 关于null 和 undifined
- ==运算规则
- 组件和模块
- 开发依赖和生产依赖
- null,undefined 的区别
- defer和async
- attribute和property的区别是什么
- 面对对象和面对过程
- JavaScript如何实现异步编程
- 对原⽣Javascript了解程度
- 什么是代码构建
- Vue数据响应式 检测变化的注意事项
- 正则表达式
- Echart设置渐变色
- npm run dev 和 npm run bulid指令分析
- Props中的default
- 路由跳转,从一级到三级,一级如果没有组件,可以配置
- Vue 2.x 对 v-if 和 v-for 的处理
- 外边距(margin)重叠示例
- vim /lib/systemd/system/nginx.service (配置Nginx)
- 扁平数组转树状数组
- vm和vc的关系
- inline-table
- 仅仅使用el-image组件中的预览功能(Element-ui)
- span可以设置margin-left和margin-right但是不可以设置margin-top和margin-bottom
- vue自定义指令的生命周期
- Vue跳转新页面后回到顶部
- $nextTick()的使用
- promise解决重复调用同一接口
- v-html可能导致的问题
- vue中父组件触发子组件函数
执行上下文创建阶段:
- 收集变量形成变量对象
- 确定this指向
- 形成作用域链
this也属于变量对象中的一员
上下文分为全局执行上下文和函数执行上下文
new关键词的作用:
-
在内存中开辟空间
-
改变this指向
-
执行函数体代码
-
返回this指向
-
函数在定义的时候就决定了它调用的时候的上级作用域链是谁
-
构造函数的方法是给自己用的,原型上的方法是给实例用的
-
实例可以访问到原型链上的方法,但是不能访问当构造函数身上的方法
什么是event loop:
- 事件循环(event loop)就是 任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。
- 宏任务(macrotask)和微任务(microtask)
- 常见宏任务:setTimeout setInterval setImmediate(node环境下) xhr(发送网络请求) callback
- 常见微任务:Promise.then(), .then 中的逻辑是微任务 process.nextTick(node环境)
- 执行顺序:微任务 -> 页面渲染 -> 宏任务
深拷贝和浅拷贝:
-
浅拷贝(引用类型数据)
-
直接赋值 =只要赋值对象的属性值变动了,原来对象的属性值就会改变。
-
Object.assign({}, obj01) 只做了一层深拷贝,内部的引用类型数据变动还是会引起原来对象的属性值变动。
-
-
展开运算符 {…obj01}
- 深拷贝
- JSON.stringify()与JSON.parse()无论多少层数据嵌套均能深度拷贝,互不影响。缺陷:会忽略值为function以及undefied的属性值,而且对date类型的支持也不太友好
- 函数递归复制
闭包三个特性
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
jq核心代码
- window.$ = window.jQuery = jQuery
隐式类型转化
- 转布尔
- 七个虚值(转换为布尔值都为false)undifined , null , ‘’ , NaN , +0 , -0 , false
- 其余转为布尔值都是 true 包括 正则,new Error() Symbol()都是 true
获得时间戳
- 获取Unix时间戳的毫秒数 new Date().getTime()
关于null 和 undifined
- 假如你打算把一个变量赋予对象类型的值,但是现在还没有赋值,那么你可以用null表示此时的状态(证据之一就是typeof null 的结果是’object’);相反,假如你打算把一个变量赋予原始类型的值,但是现在还没有赋值,那么你可以用undefined表示此时的状态。
==运算规则
组件和模块
-
组件:把重复的代码提取出来合并成为一个个组件,组件最重要的就是重用(复用),位于框架最底层,其他功能都依赖于组件,可供不同功能使用,独立性强。
-
模块:分属同一功能/业务的代码进行隔离(分装)成独立的模块,可以独立运行,以页面、功能或其他不同粒度划分程度不同的模块,位于业务框架层,模块间通过接口调用,目的是降低模块间的耦合,由之前的主应用与模块耦合,变为主应用与接口耦合,接口与模块耦合。
-
在webpack中会将所有的资源文件(js/json/css/img/less…)都作为模块处理.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bEQncfqH-1665641865336)(C:\Users\14777\AppData\Local\Temp\WeChat Files\d213f13709182219cfb0093e403a178.png)]
(67条消息) JavaScript梗图详解_菜鸟小铭的博客-CSDN博客_javascript 梗图
开发依赖和生产依赖
- 开发依赖:能帮助程序员加工代码的库,都是开发依赖 -D
- 生产依赖:能帮助程序员实现功能的库,都是生产依赖 -S
关于背景图和img
null,undefined 的区别
- undefined 表示不存在这个值。null 表示⼀个对象被定义了,值为“空值”
- undefined :是⼀个表示"⽆"的原始值或者说表示"缺少值",就是此处应该有⼀个值,但是还没有定义。当尝试读取时会返回 undefined 例如变量被声明了,但没有赋值时,就等于 undefined
- null : 是⼀个对象(空对象, 没有任何属性和⽅法)例如作为函数的参数,表示该函数的参数不是对象;
- 在验证 null 时,⼀定要使⽤ === ,因为 == ⽆法分别 null 和 undefined
defer和async
-
两者都是立即下载,延迟执行
-
defer并行加载js文件,会按照⻚⾯上 script 标签的顺序执⾏
-
async并行加载js文件,下载完成⽴即执⾏,不会按照⻚⾯上 script 标签的顺序执⾏
attribute和property的区别是什么
- attribute 是 dom 元素在⽂档中作为 html 标签拥有的属性;
- property 就是 dom 元素在 js 中作为对象拥有的属性。
- 对于 html 的标准属性来说, attribute 和 property 是同步的,是会⾃动更新的但是对于⾃定义的属性来说,他们是不同步的
面对对象和面对过程
- 面对对象:以功能划分问题
- 面对过程:以过程划分问题
JavaScript如何实现异步编程
- 回调函数
- Promise
- 订阅/发布
- 事件监听
对原⽣Javascript了解程度
- 数据类型、运算、对象、Function、继承、闭包、作⽤域、原型链、事件、 RegExp 、JSON 、 Ajax 、 DOM 、 BOM 、内存泄漏、跨域、异步装载、模板引擎、前端 MVC 、路由、模块化Canvas 、 ECMAScript
什么是代码构建
- 把开发环境代码转换成生产环境代码
Vue数据响应式 检测变化的注意事项
由于 JavaScript 的限制,Vue不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。
- 对于对象
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在data
对象上存在才能让 Vue 将它转换为响应式的。例如:
var vm = new Vue({
data:{
a:1 // `vm.a` 是响应式的
}
})
vm.b = 2 // `vm.b` 是非响应式的
想要添加响应式property,vue提供了Vue.set 和 vm.$set
Vue.set(object,propertyName,value)
或
vm.$set(object,propertyName,value)
Vue.set(vm.someObject, 'b', 2)
//或则
this.$set(this.someObject,'b',2)
有时你可能需要为已有对象赋值多个新 property,比如使用Object.assign()
或_.extend()
。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
- 对于数组
Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set 第一种方式
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice 第二种方式
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用vm.$set
实例方法,该方法是全局方法Vue.set
的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用splice
:
vm.items.splice(newLength)
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
正则表达式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGQHnRaR-1665641865336)(C:\Users\14777\Desktop\Vue面试\笔记\img\1470710362109508.gif)]
Echart设置渐变色
itemStyle: {
normal: {
lineStyle: { color: '#2db7f5' }, //设置线条颜色
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: '#2db7f5', // 0% 处的颜色
}, {
offset: 1, color: '#fff', // 100% 处的颜色
}],
},
},
}
},
npm run dev 和 npm run bulid指令分析
个人理解:run dev就是在把打包文件放在了本地缓存,创建了一个express服务器,当向本地访问时返回这个包,run build就是在内存中创建了一个包,便于后期部署
package.json里面。
“dev”:“node build/dev-server.js”,
“build”:“node build/build.js”,
意思是:运行"npm run dev"的时候执行的是build/dev-server.js文件,
运行"npm run build"的时候执行的是build/build.js文件。
build文件夹分析
build/dev-server.js。
npm run dev 执行的文件build/dev-server.js文件,执行了:
- 1、检查node和npm的版本。
- 2、引入相关插件和配置。
- 3、创建express服务器和webpack编译器。
- 4、配置开发中间件(webpack-dev-middleware)和热重载中间件(webpack-hot-middleware)。
- 5、挂载代理服务和中间件。
- 6、配置静态资源。
- 7、启动服务器监听特定端口(8080)。
- 8、自动打开浏览器并打开特定网址(localhost:8080)。
说明: express服务器提供静态文件服务,不过它还使用了http-proxy-middleware,一个http请求代理的中间件。前端开发过程中需要使用到后台的API的话,可以通过配置proxyTable来将相应的后台请求代理到专用的API服务器。
build/webpack.base.conf.js
dev-server依赖的webpack配置是webpack.dev.conf.js文件,
测试环境下使用的是webpack.prod.conf.js
webpack.dev.conf.js中又引用了webpack.base.conf.js
webpack.base.conf.js主要完成了下面这些事情:
- 配置webpack编译入口
- 配置webpack输出路径和命名规则
- 配置模块resolve规则
- 配置不同类型模块的处理规则
- 这个配置里面只配置了.js、.vue、图片、字体等几类文件的处理规则,如果需要处理其他文件可以在module.rules里面配置。
Props中的default
-
默认使用
props: { obj: { type:Object, default:() => ({}) }, arr: { type:Array, default:() => ([]) }
-
为什么需要通过函数的形式
因为对象和数组都是属于我们的复杂类型,在进行访问指向的时候我们使用的是对象和数组的地址,而不像基础数据类型。那如果我们没有通过函数的形式去设置对象和数据类型的默认值,而是直接采用{}和[],如果多个使用该组件的地方因为没有传递props而使用了默认值,假设其中一个地方我们修改了默认的值(不推荐,会报警告,不符合反向数据流),那么其他的地方由于指向的是同一个内存中的引用地 址,其他地方的显示值也会发生修改我们使用了函数的形式去返回,保证每次函数执行出来的都是返回一个新的对象,这样就不会出现上面所有的情况
路由跳转,从一级到三级,一级如果没有组件,可以配置
name: 'other',
path: 'other',
redirect: '/other/other1',
// 由于没有这个组件,向下寻找
component: { render: e => e('router-view') },
Vue 2.x 对 v-if 和 v-for 的处理
在 Vue 2.x 中, 如果在一个元素上同时使用 v-for 和 v-if,Vue 会优先渲染 v-for。
因此,下面这个做法仍然是可以正常渲染的。
<template>
<ul>
<li v-for="i in 10" :key="i" v-if="i > 5" >{{ i }}</li>
</ul>
</template>
因为v-for 先被渲染出来,所以v-if 表达式里面的变量i可以正常获取。但是这样就造成资源的浪费,先通过 v-for 渲染出来,然后又立即将部分DOM给移除了。
外边距(margin)重叠示例
外边距重叠是指两个垂直相邻的块级元素,当上下两个边距相遇时,起外边距会产生重叠现象,且重叠后的外边距,等于其中较大者。
另一个重叠现象是当一个元素包含在另一个元素之中时,子元素与父元素之间也会产生重叠现象,重叠后的外边距,等于其中最大者。
同理,如果一个无内容的空元素,其自身上下边距也会产生重叠。
外边距重叠的意义
外边距的重叠只产生在普通流文档的上下外边距之间,这个看起来有点奇怪的规则,其实有其现实意义。设想,当我们上下排列一系列规则的块级元素(如段落P)时,那么块元素之间因为外边距重叠的存在,段落之间就不会产生双倍的距离。
防止外边距重叠解决方案:
虽然外边距的重叠有其一定的意义,但有时候我们在设计上却不想让元素之间产生重叠,那么可以有如下几个建议可供参考:
- 外层元素padding代替内层元素的padding
- 内层元素透明边框 border:1px solid transparent;
- 内层元素绝对定位 postion:absolute:
- 外层元素 overflow:hidden;
- 内层元素 加float:left;或display:inline-block;
- 内层元素padding:1px;
vim /lib/systemd/system/nginx.service (配置Nginx)
[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp= true
[Install]
WantedBy=multi-user.target
[Unit]: 服务的说明
Description:描述服务
After:描述服务类别
[Service]服务运行参数的设置
Type=forking是后台运行的形式
ExecStart为服务的具体运行命令
ExecReload为重启命令
ExecStop为停止命令
PrivateTmp=True表示给服务分配独立的临时空间
注意:[Service]的启动、重启、停止命令全部要求使用绝对路径
[Install]运行级别下服务安装的相关设置,可设置为多用户,即系统运行级别为3
保存退出。
扁平数组转树状数组
convert(list) {
const result = []
const map = list.reduce((pre, cur) => {
pre[cur.id] = cur
return pre
}, {})
for (let item of list) {
if (item.parentId === 100) {
result.push(item)
continue
}
if (item.parentId in map) {
const parent = map[item.parentId]
parent.children = parent.children || []
parent.children.push(item)
}
}
return result
}
vm和vc的关系
inline-table
inline-table得到的是,外面是“内联盒子”,里面是“table盒子”。
得到的是一个可以和文字在一行中显示的表格。
tbody {
width: 100%;
display: inline-table;
}
td {
//给td强制换行,因为用了inline-table后width不再生效
word-break: break-all;
}
仅仅使用el-image组件中的预览功能(Element-ui)
<el-image-viewer v-if="dialogVisible" :on-close="()=>{dialogVisible=false}" :url-list="imgList" style="z-index: 9999;" />
components() {
'el-image-viewer': () => import('element-ui/packages/image/src/image-viewer')
},
data() {
return {
dialogVisible: false,//控制预览图片显示和隐藏
imgList:[]//预览列表
};
},
span可以设置margin-left和margin-right但是不可以设置margin-top和margin-bottom
span的margin-left和margin-right是本来就有效的,跟display属性没有关系。
vue自定义指令的生命周期
- 全局注册
Vue.directive('focus', {
// 指令绑定的元素被插入到 DOM 中时
inserted (el) {
// 聚焦元素
el.focus()
}
})
Vue.directive('focus', {
bind: function(el){}, //每当指令绑定到元素上,立即执行 bind 函数。注意:绑定时 DOM 树还未被构建。
inserted: function(el){}, //inserted 表示元素插入到 DOM 中的时候(已经插入),执行 inserted 函数
updated: function(el){} //当VNode更新时,会执行 updated
componentUpdate : function(el){} //被绑定的元素所在模板完成一次更新更新周期的时候调用
unbind: function(el){} //只调用一次,指令与元素解绑的时候调用
})
- 局部注册
export default {
directives: {
inserted (el) {
el.focus()
}
}
}
指令的钩子 | 描述 |
---|---|
bind | 只调用一次,指令第一次绑定到元素时调用 |
inserted | 被绑定元素插入父节点时调用 |
update | 所在组件的VNode 更新时调用,可能发生在其子VNode更新之前 |
componentUpdated | 指令所在组件的VNode及其子VNode全部更新后调用 |
unbind | 只调用一次,指令与元素解绑时调用 |
钩子函数的参数
钩子函数可以接受一些参数,用来指定要操作的对象或事件
el
:指令所绑定的元素,可以用来直接操作 DOM 。- binding:一个对象,包含以下属性:
name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。vnode
:Vue 编译生成的虚拟节点。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
除了
el
之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset来进行。
Vue跳转新页面后回到顶部
第一种方法:
main.js中配置。
router.afterEach((to,from,next) => {
window.scrollTo(0,0);
});
第一种方法:
在创建router实例时,做如下的配置,savedPosition当且仅当通过浏览器的前进/后退按钮触发时才可用。
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return {x: 0, y: 0}
}
第二种方法:
找到入口切换路由的页面App.vue文件下,添加watch事件,全局监听路由。
使用watch 监听$router的变化,
watch: {
'$route': function(to,from){
document.body.scrollTop = 0
document.documentElement.scrollTop = 0
}
}
$nextTick()的使用
一、NextTick是什么
官方对其的定义
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
可以理解成,Vue
在更新 DOM
时是异步执行的。当数据发生变化,Vue
将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
举例一下:
Html
结构
<div id="app"> {{ message }} </div>
构建一个vue
实例
const vm = new Vue({
el: '#app',
data: {
message: '原始值'
}
})
修改message
this.message = '修改后的值1'
this.message = '修改后的值2'
this.message = '修改后的值3'
这时候想获取页面最新的DOM
节点,却发现获取到的是旧值
console.log(vm.$el.textContent) // 原始值
这是因为message
数据在发现变化的时候,vue
并不会立刻去更新Dom
,而是将修改数据的操作放在了一个异步操作队列中
如果我们一直修改相同数据,异步操作队列还会进行去重
等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿来进行处理,进行DOM
的更新
- 为什么要有nexttick?
举个例子
{{num}}
for(let i=0; i<100000; i++){
num = i
}
如果没有 nextTick
更新机制,那么 num
每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick
机制,只需要更新一次,所以nextTick
本质是一种优化策略
二、使用场景
如果想要在修改数据后立刻得到更新后的DOM
结构,可以使用Vue.nextTick()
第一个参数为:回调函数(可以获取最近的DOM
结构)
第二个参数为:执行函数上下文
// 修改数据
vm.message = '修改后的值'
// DOM 还没有更新
console.log(vm.$el.textContent) // 原始的值
Vue.nextTick(function () {
// DOM 更新了
console.log(vm.$el.textContent) // 修改后的值
})
组件内使用 vm.$nextTick()
实例方法只需要通过this.$nextTick()
,并且回调函数中的 this
将自动绑定到当前的 Vue
实例上
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '修改后的值'
})
$nextTick()
会返回一个 Promise
对象,可以是用async/await
完成相同作用的事情
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
await this.$nextTick()
console.log(this.$el.textContent) // => '修改后的值'
promise解决重复调用同一接口
前端实现多次调用同一个接口,所有数据均成功返回后,才可继续执行下面的代码:
- 封装请求数据方法
getData(param) {
return new Promise((resolve, reject) => {
this.$axios
.get(`/xx/xx/xxpath/${param}`)
.then(res => {
let content = res.data
resolve(content)
})
})
}
- 收集所有请求结果
let promiseList = []
list.forEach(item => {
promiseList.push(this.getPhotoData(item))
})
Promise.all(promiseList).then(value => {
console.log(value)
//...下一步其他操作
})
v-html可能导致的问题
Vue
中的v-html
指令用以更新元素的innerHTML
,其内容按普通HTML
插入,不会作为Vue
模板进行编译,如果试图使用v-html
组合模板,可以重新考虑是否通过使用组件来替代。
描述:
- 易导致XSS攻击
v-html
指令最终调用的是innerHTML
方法将指令的value
插入到对应的元素里,这就是容易造成xss
攻击漏洞的原因了。Vue
在官网对于此也给出了温馨提示,在网站上动态渲染任意HTML
是非常危险的,因为容易导致XSS
攻击,只在可信内容上使用v-html
,永不用在用户提交的内容上。 关于XSS
,跨站脚本攻击XSS
,是最普遍的Web
应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。当动态页面中插入的内容含有这些特殊字符如<
时,用户浏览器会将其误认为是插入了HTML
标签,当这些HTML
标签引入了一段JavaScript
脚本时,这些脚本程序就将会在用户浏览器中执行。当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS
漏洞。
- 不作为模板编译
v-html
更新的是直接使用元素的innerHTML
方法,内容按普通HTML
插入,不会作为Vue
模板进行编译,如果试图使用v-html
组合模板,可以重新考虑是否通过使用组件来替代。另外后端返回<script>
标签中的代码是不会直接执行的,这是浏览器的策略,如果需要的话可以在$nextTick
回调中动态创建<script>
标签然后src
引入代码url
即可。
- scoped样式不能应用
在单文件组件里,scoped
的样式不会应用在v-html
内部,因为那部分HTML
没有被Vue
的模板编译器处理,如果你希望针对v-html
的内容设置带作用域的CSS
,你可以替换为CSS Modules
或用一个额外的全局<style>
元素手动设置类似BEM
的作用域策略。此外提一下关于样式隔离的话,Shadow DOM
也是个不错的解决方案。关于CSS Modules
以及BEM
命名规范可以参考下面的链接。
vue中父组件触发子组件函数
//子组件
<child-div ref='child'></child-div>
//子组件
ethods:{
handle () {console.log("我是子组件")}
}
//父组件
this.$refs['child'].handle()
this.$refs.handle()
//ref='child'代表子组件在父组件的名字