防抖和节流函数的使用
作用:降低函数的执行频率
良好的插件:lodash.debounce和lodash.throttle
防抖的应用场景
window.onload = function() {
var div1 = document.getElementById("div1");
var i = 0;
var sid;
div1.onmousemove = function() {
clearTimeout(sid);
sid = setTimeout(function() {
div1.innerHTML = i;
i ++;
}, 1000);
}
}
应用场景:
- resize
- 搜索引擎ajax提交
- 元素拖拽
节流的应用场景
window.onload = function() {
var div1 = document.getElementById("div1");
var i = 0;
var flag = true;
var sid;
window.onmousemove = function() {
if (flag) {
flag = false;
sid = setTimeout(function() {
div1.innerHTML = i;
i ++;
flag = true;
}, 1000);
}
}
}
应用场景:
- 无限下拉加载
webpack性能优化
1、raw-loader实现资源内联:raw-loader会读取文件,并将文件内容作为一个字符串返回,插入到指定位置(项目优化)
(1)代码层面:页面框架初始化脚本,比如移动端flexible.js;CSS内联避免页面闪动;
(2)请求层面:减少http网络请求次数(小图片{url-loader})
2、提取公共资源:(项目优化,打包优化)
(1)html-webpack-externals-plugin实现不将基础库打包进bundle.js中,而在模板文件中使用CDN服务器自动引入
(2)SplitChunksPlugin实现将公共脚本,比如react.js,react-dom.js或者自己封装的公共代码分离出来,打包成单独的js文件,自动引入模板文件中
(3)mini-css-extract-plugin实现将CSS文件从bundle.js中提取出来,打包成单独的CSS文件
3、代码分割及动态import:使用@babel/plugin-syntax-dynamic-import实现将不是页面初始化的js代码,打包成单独的js文件,使用到的时候,进行动态import进行加载(项目优化,打包优化)
4、开启多进程打包:使用thread-loader开启多进程打包(打包优化)
5、对图片进行压缩:image-webpack-loader(项目优化,打包优化)
6、将webpack.config.js文件进行分割,以方便不同环境配置和维护(性能优化,打包优化)
7、.js文件压缩及缓存:terser-webpack-plugin(项目优化,打包优化)
8、css文件进行压缩:optimize-css-assets-webpack-plugin(项目优化,打包优化)
9、热重载(打包优化)
简历好好整
个人技能精悍一点
项目react4/5个,vue1个
antd组件封装,思路,怎么实现的,考虑因素
table封装:
首先考虑一下antd提供的table常见的配置属性有:bordered,column,dataSource,pagination,selectedItem,selectedRowKeys,rowSelection.onRow这些,像前几个可以通过父组件传值在子组件中结构出来,而rowSelection决定表格是什么样式的,像radio单选,checkbox复选框,或者就是一个普通表格,这里我们需要处理一下,如果传过来是radio或者checkbox,就让他显示相应的表格,如果什么这个属性什么也没有穿,就让它等于null;然后onRow里面也需要判断一下这个属性是不是null,如果是,直接return false,否则再往下处理;如果是radio的话,当我们点击换选中的项时,就直接将index赋值给selectedRowKeys,将record赋值给selectedItem,然后通过调用父组件传过来的一个方法将数据传回去,更新一下state中的选中项和选中项的index;如果是checkbox就需要判断一下selectedIds是不是空,如果是空,说明是第一次选中项,将选中项,选中项的index和id添加到selectedItems,selectedRowKeys,selectedIds里面,如果seletedIds不是空,说明已经选中一项了,再点击需要判断是取消还是又选中了一项,然后进行删除或者新增,之后在调用父组件传过来的方法去更新父组件的state
import React, { Component } from 'react';
import { Table } from 'antd';
import Utils from '../../utils/utils';
class ETable extends Component {
onRowClick = (record, index) => {
let { rowSelection } = this.props;
if (rowSelection === 'radio') {
let rowSelectedKeys = [index];
let selectedItem = record;
this.props.updateSelectedItem(rowSelectedKeys, selectedItem);
} else if (rowSelection === "checkbox"){
let selectedRowKeys = this.props.selectedRowKeys;
let selectedItem = this.props.selectedItem;
let selectedIds = this.props.selectedIds;
if (selectedIds) {
const i = selectedIds.indexOf(record.id);
if (i === -1) {
selectedIds.push(record.id);
selectedRowKeys.push(index);
selectedItem.push(record);
} else {
selectedIds.splice(i, 1);
selectedRowKeys.splice(i, 1);
selectedItem.splice(i, 1);
}
} else {
selectedIds = [record.id];
selectedRowKeys = [index];
selectedItem = [record];
}
this.props.updateSelectedItem(selectedRowKeys, selectedItem, selectedIds);
}
}
tableInit = () => {
let row_selection = this.props.rowSelection;
let selectedRowKeys = this.props.selectedRowKeys;
const rowSelection = {
type: "checkbox",
selectedRowKeys,
onChange: this.onSelectChange
}
if (row_selection === false || row_selection === undefined) {
row_selection = false;
} else if (row_selection === 'radio') {
rowSelection.type = 'radio';
}
return <Table
bordered
{...this.props}
rowSelection={row_selection?rowSelection:null}
onRow={(record,index) => {
if (!row_selection) {
return ;
}
return {
onChange: () => {this.onRowClick(record, index)},
onClick: () => {this.onRowClick(record, index)},
};
}}
/>
}
render() {
return (
<div>
{this.tableInit()}
</div>
);
}
}
export default ETable;
form封装:
首先form封装我们想把它做成配置式的,通过一个数组将要渲染的表单元素传给form组件,里面应该有我们要渲染的表单元素的信息,比方说type类型、field、label、list、placeholder、width等,表单类型不一样,首先需要判断一下type类型,然后进行不同的处理,之后把它放进一个数组当中;之后返回这个数组,render出来.
import React, { Component } from 'react';
import { Input, Select, Form, Button, Checkbox, Radio, DatePicker } from 'antd';
import Utils from '../../utils/utils';
const Option = Select.Option;
const FormItem = Form.Item;
class FilterForm extends Component {
handleFilterSubmit = () => {
let fieldsValue = this.props.form.getFieldsValue();
this.props.filterSubmit(fieldsValue);
}
initialFormList = () => {
const { getFieldDecorator } = this.props.form;
const { formList } = this.props;
const formItemList = [];
if (formList && formList.length > 0) {
formList.forEach((item, i) => {
let { label, field, placeholder, width, list, type } = item;
let initialValue = item.initialValue || '';
if (type === "时间查询") {
const begin_time = <FormItem label="订单时间">
{
getFieldDecorator("begin_time")(
<DatePicker showTime={true} format="YYYY-MM-DD HH:mm:ss" placeholder="请选择时间"></DatePicker>
)
}
</FormItem>
formItemList.push(begin_time);
const end_time = <FormItem label="~" colon={false}>
{
getFieldDecorator("end_time")(
<DatePicker showTime={true} format="YYYY-MM-DD HH:mm:ss" placeholder="请选择时间"></DatePicker>
)
}
</FormItem>
formItemList.push(end_time);
}else if (type === "SELECT") {
const SELECT = <FormItem label={label} key={item.field}>
{
getFieldDecorator([field],{
initialValue
})(
<Select
placeholder={placeholder}
style={{width}}
>
{ Utils.getOptionList(list) }
</Select>
)
}
</FormItem>
formItemList.push(SELECT);
} else if (type === 'INPUT') {
const INPUT = <FormItem label={label}>
{
getFieldDecorator([field],{
initialValue
})(
<Input type="text" placeholder={placeholder} style={{width: width}} />
)
}
</FormItem>
formItemList.push(INPUT);
} else if (type === 'CHECKBOX') {
const CHECKBOX = <FormItem label={label}>
{
getFieldDecorator([field],{
valuePropName: 'checked',
initialValue
})(
<Checkbox>{label}</Checkbox>
)
}
</FormItem>
formItemList.push(CHECKBOX);
}
})
}
return formItemList;
}
render() {
return (
<Form layout="inline">
{this.initialFormList()}
<FormItem>
<Button type="primary" onClick={this.handleFilterSubmit}>查询</Button>
<Button onClick={this.reset}>重置</Button>
</FormItem>
</Form>
);
}
reset = () => {
this.props.form.resetFields();
}
}
export default Form.create({})(FilterForm);
公共组件封装
时间格式化组件封装
formateDate(time) {
if (!time) return;
let date = new Date(time);
let hours = date.getHours() < 10 ? "0"+date.getHours() : date.getHours();
let minutes = date.getMinutes() < 10 ? "0"+date.getMinutes():date.getMinutes();
let seconds = date.getSeconds()<10?"0"+date.getSeconds():date.getSeconds();
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + hours + ':' + minutes + ':' + seconds;
},
table中的pagination
pagination(data, callback) {
return {
onChange: (current) => {
callback(current);
},
current: data.result.page,
pageSize: data.result.page_size,
total: data.result.total,
showTotal: () => {
return `共${data.result.total}条数据`
},
showQuickJumper: true
}
},
form中下拉菜单的封装
getOptionList(data) {
if (!data) {
return [];
}
let options = [<Option value="0" key="all_key">全部</Option>];
data.map(item => {
options.push(<Option value={item.id} key={item.id}>{item.name}</Option>);
});
return options;
},
axios封装
static ajax(options) {
return new Promise((resolve, reject) => {
axios({
url: options.url,
method: 'get',
timeout: 5000,
params: (options.data && options.data.params) || ''
}).then((response) => {
console.log(response);
if (response.status === 200) {
resolve(response.data);
} else {
reject(response.data);
}
})
})
}
跨域封装
static jsonp(options) {
return new Promise((resolve, reject) => {
JsonP(options.url, {
param: 'callback'
}, function (err, response) {
console.log(response);
console.log(err);
if (response.status === 'success') {
resolve(response);
} else {
reject(response.messsage);
}
})
})
}
自我介绍2分钟:年龄,毕业学校,工作年限,前一家公司规模和开发模式,开发中的问题,不要说基础问题,前后端交互问题,什么公司说明白(建站/外包/外派)
问项目的话挑一个比较熟的,(项目目的,负责版块,用到的技术,遇到的问题)
数据类型分类、区别
基本数据类型是保存在栈内存中的简短数据段,数据大小是确定的,内存可以分配,我们操作、拷贝的是变量的值
引用数据类型在栈内存中保存的是数据在堆内存的地址,数据保存在堆内存中,我们操作、拷贝的是变量的地址
栈内存和堆内存的区别:栈内存和堆内存都是存储数据的地方;栈内存由操作系统自动分配和释放,速度快,使用方便,无法手动控制,内存空间有限;堆内存需要手动申请和释放,一般通过new关键字来分配,分配的速度慢,地址不连续,容易碎片话化,内存空间几乎没有限制,当我们使用new关键字来申请一块堆内存时,系统会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆节点,将这个节点从空间内存地址链表中移除,并将该节点的地址分配给程序,由于需要手动销毁,容易造成内存泄漏;
垃圾回收机制
浏览器会每隔一段时间查找一次那些不再使用的变量占用的内存,将其回收。
两种方法:标记清除和引用计数。
造成内存泄漏的场景
指一块被分配的内存既不被使用,又没有回收,直接进程结束才销毁的内存
1.意外的全局变量会会造成内存泄漏,比方说没有使用var,let,const关键字创建的变量
2.对象的互相引用
3.遗忘的定时器:setTimeout和setInterval是由浏览器专门线程来维护他的生命周期,当某个页面使用了定时器,当页面关闭时,并且没有手动关闭,这些定时器还是存在的,当定时器内部使用了回调函数,且回调函数调用了DOM元素,即使页面销毁了,这些东西还保存在内存中,造成内存泄漏。
4.使用不当的闭包。当一个函数内部返回另一个函数时,并且这个函数内部引用了外部函数词法环境中的变量,而返回的函数又被其他生命和周期的东西所占有,就会导致外部函数虽然执行完了,但还是无法被回收,滥用闭包就会造成内存泄漏。
5.遗漏的DOM元素
6.网络回调
常见的闭包场景、闭包实现是为了干什么、实现的方式有哪些(redux是闭包吗)
概念:一个函数内部的能够读取自己所有变量和方法的函数
场景:
- 定时器中的使用setTimeout:用闭包可以实现给function函数传值
- 匿名自执行函数
- 封装,实现面向对象
作用:
- 避免全局变量的污染
- 使某个变量或者函数长期驻扎在内存中,不被立即回收机制回收
JS中保存状态的方式
JS中检测对象是否反生改变的方法(proxy,Object中的一个方法,自己封装函数)
1.VueJS 则使用 ES5 提供的 Object.defineProperty(obj,key,{get(){},set(){}}) 方法
2. Proxy拦截器
3. 自己写函数
检测数据类型的方法
- typeof
- Object.prototype.toString.call()
- instanceOf()
弹性布局,简单说一下弹性盒子
是CSS3一种新的布局方式,以适应不同的屏幕大小及设备类型
5. rem用过吗?之前的项目是你自己封装的还是?你担任什么职位?你开发的整项目还是几个页面?给你几个业务面开发的话,你自己的页面开发完了如何和其他人的合并到一起?(开发模式,很重要)(项目基础配置都有了,但是我也了解)
6. 整个项目的开发的时候你会注意哪些问题?(看一下项目构建用到的包,react版本等,版本一般向下兼容,低版本的访问不了高版本的;可能自己用到的包项目里没有;尽量不要动别人的代码,否则别人提交时会报错:本地和远程不同步;看公共机制,利用utils封装好的公共机制可以简化自己代码,提高开发效率;自己组件的性能优化)
7. react性能优化从哪几方面着手?低版本不支持pureComponent和react.memo怎么办?
- pureComponent、react.memo
- react hooks
- key
- bind(this)指向写在constructor里面
什么会造成页面刷新?
- state和props的改变
- 父组件重新render
- 调用了setState后无论数据是否改变都会引起重新render
setState是同步还是异步?为什么是异步?
异步的,可以提高页面的渲染性能
ES6新增的API
- let const 关键字
- 箭头函数
- 类
- 字符串模板
- 结构
- 不定参数
- 拓展参数
- for of遍历
- Set
- Map数据结构
- 数据结构默认的iterator接口
- generator函数
- 严格模式
- Module
- Proxy
- Symbol
- 模板字符串
- Promise
diff算法和虚拟DOM,key值是必须的嘛?不加会怎么样?你会用什么绑定key值?他俩实现了什么?为什么使用他俩?
虚拟DOM就是js对象,具有批处理和高效的diff算法,他可以确保我们只对界面上真正变化的部分进行实际的DOM操作;
diff算法:在页面刚开始render的时候,会创建一棵DOM树,当我们的state或者props发生改变的时候会重新render一棵DOM树,而react基于两点假设,相同的组件产生相同的DOM结构,不同的组件产生不同的DOM结构,同一层次的节点可以通过唯一的key值进行区分
重绘和回流?那些问题会造成重绘?(CSS问题,很重要)
当页面上的元素改变但是不影响页面布局时,就会引发重绘,这时只需要ui层面的重新绘制,性能损耗较小,如字体颜色、背景色等改变;而当元素的几何属性发生变化,例如大小尺寸、位置等,或者浏览器窗口大小和DOM树结构发生变化会引发重排,又称回流。
重排优化:减少页面上的DOM操作
- 多次DOM元素的样式操作一次完成
- 批量添加DOM元素一次插入
- 将position设置成absolute或者fixed,或者display设置成none,使其脱离文档流或者先不渲染
- 尽量在内存中操作节点
节流和防抖(setState做了防抖)
- .
异步对象(Promise,Generator,回调函数:ajax,setTimeout,setInterval,async,await,发布订阅模式,事件监听)
ajax放在哪?为什么放在componentDidMount()里边?props在哪里可以访问?(render,setState,mapStateToProps,mapDispatchToProps)
15.6之前有getDefaultProps和getInitialState,新版本没有,ES6中删除
说一下路由;你配置路由怎么做;防止重复匹配的方法;路由传递的3个属性:match、history、location;
this.props.match.params
new URLSearchParams(this.props.location.search)
- 什么是高阶组件?(把组件当做参数传递进去,进行处理,再return出来)
- 对redux的理解;怎么做异步;说一下saga是怎么做的;redux怎么保存状态的;创建redux的时候,redux是怎么做状态保存的(initialState怎么保存下来的?闭包,createStore返回一个对象,对象里的getState是什么?就是initialState)
- ts必问问题:类型断言、类型检测、类型逐步接、类型推断、接口和泛型(重点)
- 从我们输入url到页面呈现发生了什么?(越详细越好)dns域名解析,进行客户端和服务器的连接:发生三次握手(发送报文,响应报文,客户端再次发送报文,连接),服务器发送html文档,里面有DOM,特殊标签link的href和src,单独隔离出来,添加到tastquery队列,再去请求这些资源,请求回来以后,首先创建一个CSSrenderTree,js回来直接执行,CSSTREE和DOMTREE结合,形成一个renderTree,期间,js回来可能会在link之前,但是js直接执行,这时候会发生css阻塞(会问,浏览器有很多线程,jui线程,ajax请求线程,计时器线程,清除计时器线程,js线程优先级最高,jui排在后面,所以会发生阻塞);断开连接,4次挥手
- 说一下什么是dva?状态用model管理;effects管理一部,用generator实现
- 介绍一下umi:umi是云谦前辈对dva再次封装,他是可插拔式的一个应用,比如说我们相拥dva,只需要在创建项目的时候选择启用就行了,包括antd;他的路由比较特殊,他是配置式路由
- vue和react的区别:vue就是一个模板,react开发更倾向于原生
- v-model使用思路:用proxy代理实现双向绑定
- vue性能优化:去哪网的keep-alive文件
- 自己搭建过一个项目吗?webpack配置项
- modules和plugins的区别
- loder的配置规范
- 优化在那个配置项里面做
- 项目优化有哪些优化?页面优化?不要搞混了
- 项目优化你怎么做?(公共机制,基础库代码单独分离出来,资源内联)页面优化是开发的优化,项目优化是webpack里面的优化
- 怎么解决bundlejs文件过大的问题(loable-able插件,代码分割和动态加载,html-webpack-externals-plugin,SplitThunksPlugin)
- 简述一下git,为什么要用git
- es6新增的特性
- Promise
- es6中的set使用:利用Set去重得到的不是数组结构,需用Array.from()进行处理;其内部使用sameValueZero Equality算法实现