1
17

一些可能会问的面试内容

前端 17 1138 1
发表于: 2022-05-20 20:11:34

简介: 暂不全,会持续更新

模块化

除了esm是es规范化,其余都是约定俗成的一种规范,即本质上都是通过js实现的模块化,大家都根据某一个规范写,逐渐流行起来后才算得上是模块化。

接触过的,esm(es6官方),commonjs(node官方),umd(库打包兼容)

ESM(es6官方)

ECMAScript Module,es6模块化

  1. 使用 import 导入模块;
  2. 使用 export 导出模块;

CommonJS(node官方)

CommonJS 在NodeJS 环境用,不适用于浏览器;

  1. 使用 exports.xx = … 或者 module.exports ={} 暴露模块;
  2. 使用 require() 方法引入一个模块;
  3. require()是同步执行的;

AMD(Require.js)

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

  1. 使用 define(…) 定义一个模块;
  2. 使用require(…) 加载一个模块;

CMD(Sea.js)

CMD(Common Module Definition - 通用模块定义)规范主要是Sea.js推广中形成的,一个文件就是一个模块,可以像Node.js一般书写模块代码。主要在浏览器中运行,当然也可以在Node.js中运行。

它与AMD很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。

UMD(通用)

全称 Universal Module Definition(万能模块定义),从名字就可以看出 UMD 做的是一统的工作。Webpack 打包代码就有 UMD 这个选项。

主要是兼容commonjs和amd

  1. 判断是否有define以及define.amd,兼容amd模块化
  2. 判断是否有exports和module,或者只有exports,兼容node模块化
  3. 如果都没有则挂载到全局变量里面(window或者global)
  4. 疑问:umd有兼容cmd吗。还是说cmd和amd类似,兼容amd约等于也兼容了cmd了。
if(typeof exports === 'object' && typeof module === 'object')
    // CommonJS规范 node 环境 判断是否支持 module.exports 支持 require 这种方法
		module.exports = factory(require("vue"));
	else if(typeof define === 'function' && define.amd)
    // AMD 如果环境中有define函数,并且define函数具备amd属性,则可以判断当前环境满足AMD规范
		define(["vue"], factory);
	else if(typeof exports === 'object')
    // 不支持 module 但是支持 exports 的情况下使用 exports导出 是CommonJS 规范
		exports["Billd"] = factory(require("vue"));
	else
    // 都不支持则挂载到全局对象
		root["Billd"] = factory(root["Vue"]);

react生命周期

挂载,当组件实例被创建并插入 DOM 中时

componentDidMount

更新,当组件的 props 或 state 发生变化时会触发更新

componentDidUpdate

componentWillUpdate(即将过时)

卸载,当组件从 DOM 中移除时会调用如下方法:

componentWillUnmount

性能优化

防抖节流

防抖:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间,防抖重在定时器清零

function debounce(f, wait) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      f(...args);
    }, wait);
  };
}

节流:高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。节流重在加锁 ,也就是重置这个定时器。

function throttle(f, wait) {
  let timer;
  return (...args) => {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      f(...args);
      timer = null;
    }, wait);
  };
}

Ahooks

useUpdate

作用是重新渲染。

主要利用了 js 里面的{}不等于{}。使用useState定义一个初始化的空对象,然后返回一个函数,这个函数setSate一个空对象。返回把这个函数返回即可。当执行这个函数的时候,因为两个空对象的引用不一样,因此会重新渲染这个组件。

import { useState } from "react";

const useUpdate = () => {
  const [, setState] = useState({});
  return () => setState({});
};

export default useUpdate;

useLocalStorageState

作用是使用 useState 的时候同时把数据缓存到 localStorage 里。

主要实现就是对 localStorage 进行了一层 useState 包装,然后导出一个 state 和设置 state 回调函数,在调用这个回调函数的时候,同时设置 localStorage.setItem 以及更新内部的 useState。以此达到statte和localStorage同步。

useDebounce

用来处理防抖值的 Hook。

useThrottle

用来处理节流值的 Hook。

浏览器差异

js兼容性

  1. new Date()构造函数使用,‘2018-07-05’是无法被各个浏览器中,使用new Date(str)来正确生成日期对象的。 正确的用法是’2018/07/05’。

css兼容性

各大浏览器前缀

  • -moz代表firefox浏览器私有属性
  • -ms代表IE浏览器私有属性
  • -webkit代表chrome、safari私有属性
  • -o代表opera私有属性

flex布局

最后一行左对齐

https://www.zhangxinxu.com/wordpress/2019/08/css-flex-last-align/

每一行列数固定

下面案例都是基于一行4个元素。

方法1:模拟space-between和间隙

// 一行4个
.list:not(:nth-child(4n)) {
    margin-right: calc(4% / 3);
}

方法2:根据个数最后一个元素动态margin

/* 如果最后一行是3个元素 */
.list:last-child:nth-child(4n - 1) {
    margin-right: calc(24% + 4% / 3);
}
/* 如果最后一行是2个元素 */
.list:last-child:nth-child(4n - 2) {
    margin-right: calc(48% + 8% / 3);
}

每一行子项宽度不固定

方法一:最后一项margin-right:auto

/* 最后一项margin-right:auto */
.list:last-child {
    margin-right: auto;
}

方法二:创建伪元素并设置flex:auto或flex:1

/* 使用伪元素辅助左对齐 */
.container::after {
    content: '';
    flex: auto;    /* 或者flex: 1 */
}

每一行列数不固定

如果每一行的列数不固定,则上面的这些方法均不适用,需要使用其他技巧来实现最后一行左对齐。

这个方法其实很简单,也很好理解,就是使用足够的空白标签进行填充占位,具体的占位数量是由最多列数的个数决定的,例如这个布局最多7列,那我们可以使用7个空白标签进行填充占位,最多10列,那我们需要使用10个空白标签。

<div class="container">
    <div class="list"></div>
    <div class="list"></div>
    <div class="list"></div>
    <div class="list"></div>
    <div class="list"></div>
    <div class="list"></div>
    <div class="list"></div>
    <i></i><i></i><i></i><i></i><i></i>
</div>
/* 和列表一样的宽度和margin值 */
.container > i {
    width: 100px;
    margin-right: 10px;
}

如果列数不固定HTML又不能调整

Grid布局

.container {
    display: grid;
    justify-content: space-between;
    grid-template-columns: repeat(auto-fill, 100px);
    grid-gap: 10px;
}
.list {
    width: 100px; height:100px;
    background-color: skyblue;
    margin-top: 5px;
}

总结

首先最后一行需要左对齐的布局更适合使用CSS grid布局实现,但是,repeat()函数兼容性有些要求,IE浏览器并不支持。如果项目需要兼容IE,则此方法需要斟酌。

然后,适用范围最广的方法是使用空的元素进行占位,此方法不仅适用于列表个数不固定的场景,对于列表个数固定的场景也可以使用这个方法。但是有些人代码洁癖,看不惯这种空的占位的html标签,则可以试试一开始的两个方法,一是动态计算margin,模拟两端对齐,另外一个是根据列表的个数,动态控制最后一个列表元素的margin值实现左对齐。

Redux和Vuex区别

Vuex是和vue强绑定的。
Redux就是一个状态管理库,理论可以在任何地方用。
Redux所有操作都是dispatch触发action,异步操作得借助中间件,redux-thunk,redux-saga等。
Vuex同步操作是commit触发mutation,异步操作是dispatch触发action,action里面在再通过commit触发mutation更新数据

Redux

reducer

reducer名称由来,它和es6的reduce很像,第一个参数是初始化status值,第二个参数是action,reducer里面通过判断action的type来执行不同逻辑返回最新status。reducer要求是一个纯函数。

combineReducers(区分模块)

通过拆分reducer,一个reducer就是一个模块,然后通过redux导入的combineReducers合并所有reducer。这个combineReducers返回值也是一个reducer。

import { combineReducers } from 'redux';

const reducer = combineReducers({
  counterInfo: counterReducer,
  homeInfo: homeReducer
});

connect/Provider

如果是类组件,需要通过react-redux的connect来连接react和redux,通过Provider包裹。

这个connect其实是一个高阶函数,它接收mapStateToProps和mapDispatchToProps,并且返回值也是一个函数。

内部使用了React.createContext,在componentDidMount的时候subscribe监听redux的store变化并且setState。以此触发重新渲染更新组件。最后在componentWillUnmount的时候取消订阅。

import { connect } from 'react-redux';

export default connect(mapStateToProps, mapDispatchToProps)(Home);

如果是函数式组件,则不需要connect,通过react-redux的useSelector这个hooks来实现。

import { useSelector, shallowEqual } from 'react-redux';

const { banners, recommends, counter } = useSelector(state => ({
  banners: state.banners,
  recommends: state.recommends,
  counter: state.counter
}), shallowEqual);

applyMiddleware

https://redux.js.org/tutorials/fundamentals/part-4-store#middleware

import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducer'
import { print1, print2, print3 } from './exampleAddons/middleware'

const middlewareEnhancer = applyMiddleware(print1, print2, print3)

// Pass enhancer as the second arg, since there's no preloadedState
const store = createStore(rootReducer, middlewareEnhancer)

export default store

中间件作用

Redux 使用一种称为中间件的特殊插件来让我们自定义dispatch功能。

中间件在发出action和执行reducer之间添加了一些功能。

其实中间件是一个高阶函数,这个函数嵌套返回了两个函数。在这两个函数里面可以做一些操作。

自定义中间件

// Middleware written as ES5 functions

// Outer function:
function exampleMiddleware(storeAPI) {
  return function wrapDispatch(next) {
    return function handleAction(action) {
      // Do anything here: pass the action onwards with next(action),
      // or restart the pipeline with storeAPI.dispatch(action)
      // Can also use storeAPI.getState() here

      return next(action)
    }
  }
}

redux-thunk

redux的操作都是通过dispatch,默认的dispatch的reducer一般都是一个对象,因此很难做到异步,thunk这个中间件将

源码只有14行(js版本的时候就是14行,后面换成ts后多了类型和注释也只有53行)。

解释:根据自定义中间件的范式,仅仅是对action做了一个判断

  1. 如果action是函数类型,则调用action,把storeAPI,storeAPI即dispatch和getState,和额外参数extraArgument传给action并执行。
  2. 如果不是函数类型,则直接next(action)。
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

Es6新增

  1. symbol类型
  2. let/const
  3. filter、map、reduce、some、every、find、findIndex
  4. set和map
  5. Proxy代理
  6. Class类
  7. 迭代器和生成器
  8. 装饰器(还没有定案)
  9. 扩展运算符
  10. promise
  11. async/await
  12. Module模块化

Webpack

解析各种资源,样式,图片,js/jsx/ts/tsx

Eslint

热更新

CompressionPlugin===>gzip压缩

babel===》plugin-syntax-dynamic-import,路由懒加载

Externals+HtmlWebpackTagsPlugin,cdn加载第三方库

Treeshaking,移除没有引用的代码

optimization—》splitChunks,优化bundle大小

TerserPlugin,压缩js

PreloadPlugin,预加载js

Billd-UI

项目目录

|-- README.md // 说明文件
|-- babel.config.js // babel配置,当使用webpack构建umd版本时会使用该配置
|-- index.js // 所有组件的入口文件,当使用webpack构建umd版本时会使用该入口
|-- package.json
|-- postcss.config.js // postcss配置,当使用webpack构建umd版本时会使用该配置
|-- tsconfig.json
|-- build-tools // 构建目录
|   |-- getBabelCommonConfig.js // babel配置,当使用gulp构建es和lib版本时会使用该配置
|   |-- getPostcssConfig.js // postcss配置,当使用gulp构建es和lib版本时会使用该配置
|   |-- gulpfile.js // gulp配置文件
|   |-- webpack.common.js // webpack通用配置
|   |-- webpack.dev.js // webpack开发环境配置
|   |-- webpack.prod.js // webpack生产环境配置,当使用webpack构建umd版本时会使用该配置
|   |-- webpack.prod.min.js // webpack生产环境配置,当使用webpack构建umd版本的压缩版本时会使用该配置
|   |-- svgo // svgo配置
|   |   |-- svgOptions.js // svg优化选项
|   |   |-- template // svg模板
|   |       |-- icon.ejs
|   |-- utils // 常用方法
|       |-- chalkTip.js // 自定义console
|       |-- paths.js // 处理路径
|       |-- transformLess.js // 编译less
|-- components // 组件目录
|   |-- index.js // 所有组件的入口文件,当使用gulp构建es和lib版本时会使用该入口
|   |-- modal
|   |   |-- foot.jsx
|   |   |-- index.jsx // modal组件入口文件
|   |   |-- style	// modal组件样式目录
|   |       |-- index.js // modal组件样式的入口文件
|   |       |-- index.less // modal组件样式
|   |-- switch
|   |   |-- index.jsx // switch组件入口文件
|   |   |-- style // switch组件样式目录
|   |       |-- index.js // switch组件样式的入口文件
|   |       |-- index.less // switch组件样式
|-- src // 本地开发调试
|-- es // 构建好的es版本
|-- lib // 构建好的lib版本
|-- dist // 构建好的umd版本

Gulp

基于stream流的自动化构建工具

task

  • 公开任务(Public tasks) 从 gulpfile 中被导出(export),可以通过 gulp 命令直接调用。
  • 私有任务(Private tasks) 被设计为在内部使用,通常作为 series()parallel() 组合的组成部分。

gulp.src

通过glob匹配文件流

gulp.pipe

管道,传输文件流

gulp.dest

创建/输出文件流

gulp.parallel

并行执行任务

gulp.series

连续执行任务

http状态码

200,请求已经成功

201,成功,并且创建了资源(比如数据库新增了一条记录)

301,永久重定向(比如http永久重定向到https)

server {
    listen 80;
    server_name hsslive.cn;

    location / {
        # 把当前域名的请求,跳转到新域名上,域名变化但路径不变
        # permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址
        rewrite ^/(.*) https://hsslive.cn/$1 permanent;
    }
}

302,临时重定向

304,Not Modified,资源没改变,缓存

400,参数错误

401,未授权

403,权限不足

404,not found,没找到

429,频繁请求

431,请求中的首部字段的值过大,服务器拒绝接受客户端的请求。一般是post数据太多,或者是上传base64之类的。解决:nginx设置

http {
		#....
    client_header_buffer_size 10240k;
    large_client_header_buffers 6 10240k; 
  
  server {
     # ...
  }
}

500,服务器错误

502,网关错误

504,网关超时

移动端适配

Koa和express区别

相同点

两个框架都对http进行了封装。相关的api都差不多,同一批人所写。

不同点

express内置了许多中间件可供使用,而koa没有。

express包含路由,视图渲染等特性,而koa只有http模块。

express的中间件模型为线型,而koa的中间件模型洋葱模型构造中间件。

express通过回调实现异步函数,在多个回调、多个中间件中写起来容易逻辑混乱。

// express写法
app.get('/test', function (req, res) {
    fs.readFile('/file1', function (err, data) {
        if (err) {
            res.status(500).send('read file1 error');
        }
        fs.readFile('/file2', function (err, data) {
            if (err) {
                res.status(500).send('read file2 error');
            }
            res.type('text/plain');
            res.send(data);
        });
    });
});