阅读 11071

前端万字面经——进阶篇

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

此文为前端进阶篇前边已经出了基础篇

基础篇链接点击跳转

服务端编程/Ajax

JSONP 的缺点

  • JSON 只支持get,因为script 标签只能使用get 请求;
  • JSONP 需要后端配合返回指定格式的数据。

跨域(jsonp,ajax)

JSONP:ajax 请求受同源策略影响,不允许进行跨域请求,而script 标签src 属性中的链 接却可以访问跨域的js 脚本,利用这个特性,服务端不再返回JSON 格式的数据,而是 返回一段调用某个函数的js 代码,在src 中进行了调用,这样实现了跨域。

如何实现跨域

  • JSONP:通过动态创建script,再请求一个带参网址实现跨域通信。document.domain +iframe 跨域:两个页面都通过js 强制设置document.domain 为基础主域,就实现了同域。location.hash + iframe 跨域:a 欲与b 跨域相互通信,通过中间页c 来实现。三个页面,不同域之间利用iframe 的location.hash 传值,相同域之间直接js 访问来通信。window.name + iframe 跨域:通过iframe 的src 属性由外域转向本地域,跨域数据即由iframe的window.name 从外域传递到本地域。
  • postMessage 跨域:可以跨域操作的window 属性之一。
  • CORS:服务端设置Access-Control-Allow-Origin 即可,前端无须设置,若要带cookie 请求,前后端都需要设置。
  • 代理跨域:起一个代理服务器,实现数据的转发

实现一个Ajax

AJAX 创建异步对象XMLHttpRequest操作XMLHttpRequest 对象

  1. 设置请求参数(请求方式,请求页面的相对路径,是否异步)
  2. 设置回调函数,一个处理服务器响应的函数,使用onreadystatechange ,类似函数指针
  3. 获取异步对象的readyState 属性:该属性存有服务器响应的状态信息。每当readyState 改变时,onreadystatechange 函数就会被执行。
  4. 判断响应报文的状态,若为200 说明服务器正常运行并返回响应数据。
  5. 读取响应数据,可以通过responseText 属性来取回由服务器返回的数据。
var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
    if(xhr.readyState==4) {
        if(xhr.status==200) {
        console.log(xhr.responseText);
        }
    }
}
复制代码

移动web 开发

知道PWA 吗

PWA 全称Progressive Web App,即渐进式WEB 应用。一个PWA 应用首先是一个网页,可以通过Web 技术编写出一个网页应用. 随后添加上App Manifest 和Service Worker来实现PWA 的安装和离线等功能

移动布局方案

直接给大家一个链接吧超详细。 juejin.im/post/599970…

Rem, Em

rem 单位如何转换为像素值 当使用rem 单位的时候,页面转换为像素大小取决于叶根元素的字体大小,即HTML 元素的字体大小。根元素字体大小乘rem 的值。例如,根元素的字体大小为16px,那么 10rem 就等同于10*16=160px。

em 是如何转换成px 的 当使用em 单位的时候,像素值是将em 值乘以使用em 单位的元素的字体大小。例如一 个div 的字体为18px,设置它的宽高为10em,那么此时宽高就是18px*10em=180px。

.test{
    width: 10em;
    height: 10em;
    background-color: #ff7d42;
    font-size: 18px;
}
/**
一定要记住的是,em 是根据使用它的元素的font-size 的大小来变化的,而不是根据父
元素字体大小。有些元素大小是父元素的多少倍那是因为继承了父元素中font-size 的设
定,所以才起到的作用。
*/
复制代码

em 单位的继承效果 使用em 单位存在继承的时候,每个元素将自动继承其父元素的字体大小,继承的效果 只能被明确的字体单位覆盖,比如px 和vw。只要父级元素上面一直有fontsize 为em 单 位,则会一直继承,但假如自己设置了font-size 的单位为px 的时候,则会直接使用自 己的px 单位的值。

根html 的元素将会继承浏览器中设置的字体大小 除非显式的设置固定值去覆盖。所以html 元素的字体大小虽然是直接确定rem 的值, 但这个字体大小首先是来源于浏览器的设置。(所以一定要设置html 的值的大小,因 为有可能用户的浏览器字体大小是不一致的。)

当em 单位设置在html 元素上时 它将转换为em 值乘以浏览器字体大小的设置。

html{
    font-size: 1.5em;
}
/**
可以看到,因为浏览器默认字体大小为16px,所以当设置HTML 的fontsize 的值为1.5em
的售后,其对应的px 的值为16*1.5=24px
所以此时,再设置其他元素的rem 的值的时候,其对应的像素值为n*24px。
例如,test 的rem 的值为10,
*/
.test{
    width: 10rem;
    height: 10rem;
    background-color: #ff7d42;
}
/**
总结:
1. rem 单位翻译为像素值的时候是由html 元素的字体大小决定的。此字体大小会
被浏览器中字体大小的设置影响,除非显式的在html 为font-size 重写一个单位。
2. em 单位转换为像素值的时候,取决于使用它们的元素的font-size 的大小,但是有
因为有继承关系,所以比较复杂。
优缺点
em 可以让我们的页面更灵活,更健壮,比起到处写死的px 值,em 似乎更有张力,改
动父元素的字体大小,子元素会等比例变化,这一变化似乎预示了无限可能,
em 做弹性布局的缺点还在于牵一发而动全身,一旦某个节点的字体大小发生变化,那
么其后代元素都得重新计算
*/
复制代码

移动端适配1px 的问题

推荐 blog.csdn.net/weixin_4367…

  1. 用小数来写px 值(不推荐)
// IOS8 下已经支持带小数的px 值, media query 对应devicePixelRatio 有个查询值
// -webkit-min-device-pixel-ratio, css 可以写成这样
// 通过-webkit-min-device-pixel-ratio 设置。
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
.border { border: 0.333333px solid #999 }
}
// 如果使用less/sass 的话只是加了1 句mixin
// 缺点: 安卓与低版本IOS 不适用, 这个或许是未来的标准写法, 现在不做指望
复制代码
  1. flexible.js

这是淘宝移动端采取的方案, github 的地址:https://github.com/amfe/lib-flexible. 前面已经 说过1px 变粗的原因就在于一刀切的设置viewport 宽度, 如果能把viewport 宽度设置 为实际的设备物理宽度, css 里的1px 不就等于实际1px 长了么. flexible.js 就是这样 干的. <meta name=”viewport”>里面的scale 值指的是对ideal viewport 的缩放, flexible.js 检 测到IOS 机型, 会算出scale = 1/devicePixelRatio, 然后设置viewport

  1. 伪类+transform 实现
/** 对于解决1px 边框问题,我个人觉得最完美的解决办法还是伪类+transform 比较好。
原理:是把原先元素的border 去掉,然后利用:before 或者:after 重做border ,并
transform 的scale 缩小一半,原先的元素相对定位,新做的border 绝对定位。
media query
通过媒体查询,可以通过给不同分辨率的设备编写不同的样式来实现响应式的布局,比
如我们为不同分辨率的屏幕,设置不同的背景图片。比如给小屏幕手机设置@2x 图,为
大屏幕手机设置@3x 图,通过媒体查询就能很方便的实现。
但是媒体查询的缺点也很明显,如果在浏览器大小改变时,需要改变的样式太多,那么
多套样式代码会很繁琐。*/
@media screen and (min-width: 320px) {
html {
font-size: 50px;
}
}
@media
/** 方便应用广泛适用于pc 端手机页面,通常做自适应布局时我们比较常用。
缺点:相对于代码要重复很多,得知道设备的宽度,手机的分辨率很多所以麻烦了点,
不过性能方面肯定最高; 可能存在闪屏的问题
@media 处理手机和pc 端界面兼容的问题,在IE 上的访问出现问题,百度方法,找找
两种,一种是respond.js,另一种是 
css3-mediaquerieshttp://blog.csdn.net/small_tu/article/details/47317453
*/
复制代码

移动端性能优化相关经验

给大家一个参考不一一列举了移动端性能优化的方法

移动端兼容性

其他参考链接:https://zhuanlan.zhihu.com/p/28206065

手势事件

手势事件事件详解
touchstart当手指接触屏幕时触发 来代替 click 事件 来触发移动端的点击事件
touchmove当手指接触屏幕时触发 代替 scroll 事件
touchend当手指离开屏幕时触发 来代替 click 事件 来触发移动端的点击事件

2X 图3X 图适配

实际程序开发当中,我们代码中用的值是指逻辑分辨率pt,而不是像素分辨率px, 比如我们定义一个按钮的高度为45,这个45 指的是45pt 而不是45px。在非Retina 屏下1pt = 1px,4 和4.7 寸Retina 屏下1pt = 2px,5.5 和x 下1pt = 3px.我们制作不同尺寸的图片, 比如@1x 为22px,则@2x 为44px,@3x 为66px,命名分别为image.png,在项目的 Assets.xcassets 中新建New Image Set,修改名字为image,并把相应尺寸的图片拖放至相应位置。

/* 根据dpr 显示2x 图/3x 图*/
.bg-image(@url){
background-image:~"url('@{url}@2x.png')";
@media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3){
background-image:~"url('@{url}@3x.png')";
}
}
.bg-color(@color) {
background-color: @color;
}
复制代码

图片在安卓上,有些设备模糊问题

这个问题是 devicePixelRatio 的不同导致的,因为手机分辨率太小了,如果按照分辨率显示网页,字会非常小,所有苹果系统当初就把 iphone 4 的960x640像素的分辨率在网页里更改为480x320像素。这样是 devicePixelRatio = 2. 而 Android 的 devicePixelRatio 比较乱,值有1.5、2和3,为了在手机里更为清晰地显示图片, 必须使用2倍宽高的背景图来代替 img 标签(一般情况下都使用2倍) 例如:一个 div 的宽高是 100x100 背景图必须是200x200,然后设置 background-size:contain 样式,显示出来就比较清晰。

防止手机中页面放大和缩小

<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,maximum-scale=1">
复制代码

transiton 闪屏

//设置内联的元素在3D 空间如何呈现:保留
3D-webkit-transform-style:preserve-3D;
//设置进行转换的元素的背面在面对用户时是否可见:隐藏
-webkit-backface-visibility:hidden;
复制代码

上下拉动滚动条时卡顿、慢

body {
    -webkit-overflow-scrolling: touch;
    overflow-scrolling:touch;
}
复制代码

长时间按住页面出现闪退

element{
    -webkit-touch-callout: none;
}
复制代码

前端工程化

Babel 的原理是什么?

babel 的转译过程也分为三个阶段,这三步具体是:

  • 解析Parse: 将代码解析生成抽象语法树( 即AST ),即词法分析与语法分析的过程
  • 转换Transform: 对于AST 进行变换一系列的操作,babel 接受得到AST 并通babel-traverse 对其进行遍历,在此过程中进行添加、更新及移除等操作。
  • 生成Generate: 将变换后的AST 再转换为JS 代码, 使用到的模块是babel-generator

image.png

如何写一个babel 插件?

Babel 解析成AST,然后插件更改AST,最后由Babel 输出代码那么Babel 的插件模块需要你暴露一个function,function 内返回visitor

module.export = function(babel){
    return {
        visitor:{
        }
    }
}
复制代码

visitor 是对各类型的AST 节点做处理的地方,那么我们怎么知道Babel 生成了的 AST 有哪些节点呢?很简单,你可以把Babel 转换的结果打印出来, 或者这里有传送门:AST explorer

image.png

/** 这里我们看到const result = 1 + 2 中的1 + 1 是一个BinaryExpression 节点,那么在
visitor 中,我们就处理这个节点 */
var babel = require('babel-core');
var t = require('babel-types');
const visitor = {
BinaryExpression(path) {
const node = path.node;
let result;
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
// 根据不同的操作符作运算
switch (node.operator) {
case "+":
result = node.left.value + node.right.value;
break
case "-":
result = node.left.value - node.right.value;
break;
case "*":
result = node.left.value * node.right.value;
break;
case "/":
result = node.left.value / node.right.value;
break;
case "**":
let i = node.right.value;
while (--i) {
result = result || node.left.value;
result = result * node.left.value;
}
break;
default:
}
}
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number 字面量
path.replaceWith(t.numericLiteral(result));
}
}
};
module.exports = function (babel) {
return {
visitor
};
}
// 插件写好了,我们运行下插件试试
const babel = require("babel-core");
const result = babel.transform("const result = 1 + 2;",{
plugins:[
require("./index")
]
});
console.log(result.code); // const result = 3;
/** 与预期一致,那么转换const result = 1 + 2 + 3 + 4 + 5;呢?
结果是: const result = 3 + 3 + 4 + 5;
这就奇怪了,为什么只计算了1 + 2 之后,就没有继续往下运算了?
我们看一下这个表达式的AST 树
你会发现Babel 解析成表达式里面再嵌套表达式。
表达式( 表达式( 表达式( 表达式(1 + 2) + 3) + 4) + 5)
而我们的判断条件并不符合所有的,只符合1 + 2 */
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {}
/** 那么我们得改一改
第一次计算1 + 2 之后,我们会得到这样的表达式
表达式( 表达式( 表达式(3 + 3) + 4) + 5)
其中3 + 3 又符合了我们的条件, 我们通过向上递归的方式遍历父级节点
又转换成这样:
表达式( 表达式(6 + 4) + 5)
表达式(10 + 5)
15 */
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number 字面量
path.replaceWith(t.numericLiteral(result));
let parentPath = path.parentPath;
// 向上遍历父级节点
parentPath && visitor.BinaryExpression.call(this, parentPath);
}
/** 到这里,我们就得出了结果const result = 15;
那么其他运算呢: */
const result = 100 + 10 - 50>>>const result = 60;
const result = (100 / 2) + 50>>>const result = 100;
const result = (((100 / 2) + 50 * 2) / 50) ** 2>>>const result = 9;
复制代码

你的git 工作流是怎样的?

参考回答: 给自己引流一下可以参考我转载的政采云的一篇文章很详细 点此此处跳转

webpack 和gulp 区别(模块化与流的区别)

  • webpack 是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js 文件、css 文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。
  • gulp 强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task 处理的事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,来让gulp 执行这些task,从而构建项目的整个前端开发流程。

Vue

说说自己对vue的理解

Vue 是一个构建数据驱动的渐进性框架,它的目标是通过API 实现响应数据绑定和视图更新。 优点:

  1. 数据驱动视图,对真实dom 进行抽象出virtual dom(本质就是一个js 对象),并配合diff 算法、响应式和观察者、异步队列等手段以最小代价更新dom,渲染页面。
  2. 组件化,组件用单文件的形式进行代码的组织编写,使得我们可以在一个文件里编写html\css(scoped 属性配置css 隔离)\js 并且配合Vue-loader 之后,支持更强大的预处理器等功能。
  3. 由于采用虚拟dom,让Vue ssr 先天就足,强大且丰富的API 提供一系列的api 能满足业务开发中各类需求。
  4. 生命周期钩子函数,选项式的代码组织方式,写熟了还是蛮顺畅的,但仍然有优化空间(Vue3 composition-api)

缺点:

  1. 由于底层基于Object.defineProperty(vue3.0采用 new Proxy()) 实现响应式,而这个api 本身不支持IE8及以下浏览器
  2. csr 的先天不足,首屏性能问题(白屏)
  3. 由于百度等搜索引擎爬虫无法爬取js 中的内容,故spa 先天就对seo 优化心有余力不足(谷歌的puppeteer 就挺牛逼的,实现预渲染底层也是用到了这个工具)

什么是虚拟DOM?

虚拟dom 是相对于浏览器所渲染出来的真实dom 的,在react,vue 等技术出现之前, 我们要改变页面展示的内容只能通过遍历查询dom 树的方式找到需要修改的dom 然 后修改样式行为或者结构,来达到更新ui 的目的。 这种方式相当消耗计算资源,因为每次查询dom 几乎都需要遍历整颗dom 树,如果 建立一个与dom 树对应的虚拟dom 对象( js 对象),以对象嵌套的方式来表示dom 树,那么每次dom 的更改就变成了js 对象的属性的更改,这样一来就能查找js 对象 的属性变化要比查询dom 树的性能开销小。

vue 生命周期

image.png

vue 如何监听键盘事件?

//  @keyup. 方法
<template>
<input ref="myInput" type="text" value="hello world" autofocus
@keyup.enter="handleKey">
</template>
<script>
    export default {
    methods: {
        handleKey(e) {
            console.log(e)
        }
    }
}
</script>
//  addEventListener
<script>
    export default {
        mounted() {
            document.addEventListener('keyup', this.handleKey)
        },
        beforeDestroy() {
            document.removeEventListener('keyup', this.handleKey)
        },
        methods: {
            handleKey(e) {
                console.log(e)
            }
        }
}
</script>
<script>
export default {
    mounted() {
        document.addEventListener('keyup', this.handleKey)
    },
    beforeDestroy() {
        document.removeEventListener('keyup', this.handleKey)
    },
    methods: {
        handleKey(e) {
            console.log(e)
        }
    }
}
</script>
复制代码

watch 怎么深度监听对象变化

deep 设置为true 就可以监听到对象的变化
let vm=new Vue({
    el:"#first",
    data:{msg:{name:'北京'}},
    watch:{
        msg:{
            handler (newMsg,oldMsg){
                console.log(newMsg);
            },
            immediate:true,
            deep:true
        }
    }
})
复制代码

删除数组用delete 和Vue.delete 有什么区别?

  • delete:只是被删除数组成员变为empty / undefined,其他元素键值不变
  • Vue.delete:直接删了数组成员,并改变了数组的键值(对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开Vue 不能检测到属性被删除的限制)

watch 和计算属性有什么区别?

通俗来讲,既能用computed 实现又可以用watch 监听来实现的功能,推荐用computed, 重点在于computed 的缓存功能

computed 计算属性是用来声明式的描述一个值依赖了其它的值,当所依赖的值或者变量 改变时,计算属性也会跟着改变;

watch 监听的是已经在data 中定义的变量,当该变量变化时,会触发watch 中的方法。

Vue 双向绑定原理

Vue 数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)。

Vue3.0采用了 new Proxy() 可以参考https://juejin.cn/post/6950826293923414047 掘金签约的大神所写。

说一下axios怎么用? 怎么解决跨域的问题?

axios 的是一种异步请求,用法和ajax 类似,安装npm install axios --save 即可使用,请 求中包括get,post,put, patch ,delete 等五种请求方式,解决跨域可以在请求头中添加 Access-Control-Allow-Origin,也可以在index.js 文件中更改proxyTable 配置等解决跨域 问题。

在vue 项目中如何引入第三方库(比如jQuery)?

// 1、绝对路径直接引入
// 在index.html 中用script 引入
<script src="./static/jquery-1.12.4.js"></script>
// 然后在webpack 中配置external
externals: { 'jquery': 'jQuery' }
// 在组件中使用时import
import $ from 'jquery'
// 2 、在webpack 中配置alias
resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src'), 'jquery':
resolve('static/jquery-1.12.4.js') } }
// 然后在组件中import
// 3、在webpack 中配置plugins
plugins: [ new webpack.ProvidePlugin({ $: 'jquery' }) ]
// 全局使用,但在使用eslint 情况下会报错,需要在使用了$ 的代码前添加/*eslint-disable*/ 来去掉ESLint 的检查。
复制代码

Vue3.0 里为什么要用Proxy API 替代defineProperty API?

响应式优化。

  1. defineProperty API 的局限性最大原因是它只能针对单例属性做监听。Vue2.x 中的响应式实现正是基于defineProperty 中的descriptor,对data 中的属性做了遍历+ 递归,为每个属性设置了getter、setter。这也就是为什么Vue 只能对data 中预定义过的属性做出响应的原因,在Vue 中使用下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是无法做到setter 监听的,这是defineProperty 的局限性。
  2. Proxy API 的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
  3. 响应式是惰性的在Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行Object.defineProperty 把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。在Vue.js 3.0 中,使用Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。

基础用法:

let datas = {
    num:0
}
let proxy = new Proxy(datas,{
    get(target,property){
        return target[property]
    },
    set(target,property,value){
        target[property] += value
    }
})
复制代码

Vue3.0 编译做了哪些优化?

  1. 生成Block tree

Vue.js 2.x 的数据更新并触发重新渲染的粒度是组件级的,单个组件内部需要遍历该组 件的整个vnode 树。在2.0 里,渲染效率的快慢与组件大小成正相关:组件越大,渲染 效率越慢。并且,对于一些静态节点,又无数据更新,这些遍历都是性能浪费。 Vue.js 3.0 做到了通过编译阶段对静态模板的分析,编译生成了Block tree。Block tree 是一个将模版基于动态节点指令切割的嵌套区块,每个区块内部的节点结构是固定的, 每个区块只需要追踪自身包含的动态节点。所以,在3.0 里,渲染效率不再与模板大小 成正相关,而是与模板中动态节点的数量成正相关。

  1. slot 编译优化

Vue.js 2.x 中,如果有一个组件传入了slot,那么每次父组件更新的时候,会强制使子组 件update,造成性能的浪费。

Vue.js 3.0 优化了slot 的生成,使得非动态slot 中属性的更新只会触发子组件的更新。 动态slot 指的是在slot 上面使用v-if,v-for,动态slot 名字等会导致slot 产生运行时动 态变化但是又无法被子组件track 的操作。

3.diff 算法优化

Vue2.x 中的虚拟dom 是进行全量的对比。 Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对比的时候,值对比 带有patch flag 的节点,并且可以通过flag 的信息得知当前节点要对比的具体内容化。

  1. hoistStatic 静态提升

Vue2.x : 无论元素是否参与更新,每次都会重新创建。 Vue3.0 : 对不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用。

  1. cacheHandlers 事件侦听器缓存

默认情况下onClick 会被视为动态绑定,所以每次都会去追踪它的变化但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可。

讲讲 proxy

vue 的数据劫持有两个缺点:

  1. 无法监听通过索引修改数组的值的变化
  2. 无法监听object 也就是对象的值的变化

所以vue2.x 中才会有$set 属性的存在 proxy 是es6 中推出的新api,可以弥补以上两个缺点,所以vue3.x 版本用proxy 替换 object.defineproperty。

React

redux 中间件

中间件提供第三方插件的模式,自定义拦截action -> reducer 的过程。变为action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步action ,action 过 滤,日志输出,异常报告等功能。

常见的中间件:redux-logger:提供日志输出;redux-thunk:处理异步操作;redux-promise: 处理异步操作;actionCreator 的返回值是promise

React 组件的划分业务组件技术组件?

根据组件的职责通常把组件分为UI 组件和容器组件。UI 组件负责UI 的呈现,容器组件负责管理数据和逻辑。两者通过React-Redux 提供connect 方法联系起来。

React 生命周期函数

// 1. 初始化阶段
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的DOM 节点
componentDidMount:组件真正在被装载之后
// 2. 运行中状态
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据
后不更新,阻止render 调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
// 3. 销毁阶段:
componentWillUnmount:组件即将销毁
复制代码

React 性能优化是哪个周期函数?

shouldComponentUpdate 这个方法用来判断是否需要调用render 方法重新描绘dom。因 为dom 的描绘非常消耗性能,如果我们能在shouldComponentUpdate 方法中能够写出更 优化的dom diff 算法,可以极大的提高性能。

React 性能优化方案

  1. 重写shouldComponentUpdate 来避免不必要的dom 操作。
  2. 使用production 版本的React.js。
  3. 使用key 来帮助React 识别列表中所有子组件的最小变化

简述flux 思想

Flux 的最大特点,就是数据的"单向流动"。

  • 用户访问View
  • View 发出用户的Action
  • Dispatcher 收到Action,要求Store 进行相应的更新
  • Store 更新后,发出一个"change"事件
  • View 收到"change"事件后,更新页面

了解shouldComponentUpdate 吗

React 虚拟dom 技术要求不断的将dom 和虚拟dom 进行diff 比较,如果dom 树比价大, 这种比较操作会比较耗时,因此React 提供了shouldComponentUpdate 这种补丁函数,如 果对于一些变化,如果我们不希望某个组件刷新,或者刷新后跟原来其实一样,就可以 使用这个函数直接告诉React,省去diff 操作,进一步的提高了效率。

React 的工作原理

React 会创建一个虚拟DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会 通过"diffing" 算法来标记虚拟DOM 中的改变,第二步是调节(reconciliation),会用diff 的结果来更新DOM。

使用React 有何优点

  1. 只需查看render 函数就会很容易知道一个组件是如何被渲染的。
  2. JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的。
  3. 支持服务端渲染,这可以改进SEO 和性能。

类组件(Class component)和函数式组件(Functional component)之间有何不同

  1. 类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问store 并维持状态
  2. 当组件仅是接收props,并将组件自身渲染到页面时,该组件就是一个'无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

(组件的)状态(state)和属性(props)之间有何不同?

  1. State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。
  2. Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的props,但是可以把其子组件的props 放在一起(统一管理)。Props 也不仅仅是数据--回调函数也可以通过props传递。

在那个生命周期发起 ajax 请求

在React 组件中,应该在componentDidMount 中发起网络请求。这个方法会在组件第 一次“挂载”(被添加到DOM)时执行,在组件的生命周期中仅会执行一次。更重要的是, 你不能保证在组件挂载之前Ajax 请求已经完成,如果是这样,也就意味着你将尝试在 一个未挂载的组件上调用setState,这将不起作用。在componentDidMount 中发起网络 请求将保证这有一个组件可以更新了。

在React 中,refs 的作用是什么

Refs 可以用于获取一个DOM 节点或者React 组件的引用。何时使用refs 的好的示例 有管理焦点/文本选择,触发命令动画,或者和第三方DOM 库集成。你应该避免使用 String 类型的Refs 和内联的ref 回调。Refs 回调是React 所推荐的。

何为高阶组件(higher order component)?

高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑 和引导抽象。最常见的可能是Redux 的connect 函数。除了简单分享工具库和简单的 组合,HOC 最好的方式是共享React 组件之间的行为。如果你发现你在不同的地方写 了大量代码来做同一件事时,就应该考虑将代码重构为可重用的HOC。

为什么建议传递给setState 的参数是一个callback 而不是一个对象

因为this.props 和this.state 的更新可能是异步的,不能依赖它们的值去计算下一个state。

怎么阻止组件的渲染

在组件的render 方法中返回null 并不会影响触发组件的生命周期方法

当渲染一个列表时,何为key?设置key 的目的是什么

Keys 会有助于React 识别哪些items 改变了,被添加了或者被移除了。Keys 应该被赋 予数组内的元素以赋予(DOM)元素一个稳定的标识,选择一个key 的最佳方法是使用一 个字符串,该字符串能惟一地标识一个列表项。很多时候你会使用数据中的IDs 作为 keys,当你没有稳定的IDs 用于被渲染的items 时,可以使用项目索引作为渲染项的 key,但这种方式并不推荐,如果items 可以重新排序,就会导致re-render 变慢

(在构造函数中)调用super(props) 的目的是什么

在super() 被调用之前,子类是不能使用this 的,在ES2015 中,子类必须在constructor 中调用super()。传递props 给super() 的原因则是便于(在子类中)能在constructor 访问 this.props。

JSX

JSX 是JavaScript 语法的一种语法扩展,并拥有JavaScript 的全部功能。JSX 生产 React "元素",你可以将任何的JavaScript 表达式封装在花括号里,然后将其嵌入到JSX 中。在编译完成之后,JSX 表达式就变成了常规的JavaScript 对象,这意味着你可以在 if 语句和for 循环内部使用JSX,将它赋值给变量,接受它作为参数,并从函数中返回 它。

Angular

Angular 中组件之间通信的方式

Props down

  1. 调用子组件,通过自定义属性传值
  2. 子组件内部通过Input 来接收属性的值

Events up

  1. 在父组件中定义一个有参数的方法
  2. 调用子组件时,绑定自定义事件和上一步方法
  3. 子组件内部通过Output 和EventEmitter 来触发事件并传值.

Angualr 的八大组成部分并简单描述

model 是Angular 开发中的基本单位,是一个容器,可以包含组件、指令、管道等
Components 是可被反复使用的带有特定功能的视图
Templates 是经过指令和管道、组件等增强过的html
Bindings 结合着事件绑定属性绑定双向数据绑定等扩展html 的功能
Directives 分为结构性和属性型指令还有其他模块中比如路由模块中的指令等,
主要是增强html.
Pipes 可以对数据做一个筛选、过滤、格式化从而得到目的数据
Service 将组件、应用中的可共用的部分,比如数据或者方法等封装成服务以方便服用
DependencyInjection 依赖注入
复制代码

Angular 中常见的生命周期的钩子函数

ngOnChanges:当Angular 设置其接收当前和上一个对象值的数据绑定属性时响应。
ngOnInit:在第一个ngOnChange 触发器之后,初始化组件/指令。这是最常用的方法,
用于从后端服务检索模板的数据。
ngDoCheck:检测并在Angular 上下文发生变化时执行。
每次更改检测运行时,会被调用。
ngOnDestroy:在Angular 销毁指令/组件之前消除。取消订阅可观察的对象并脱离
事件处理程序,以避免内存泄漏。
组件特定的hooks:
ngAfterContentInit:组件内容已初始化完成
ngAfterContentChecked:在Angular 检查投影到其视图中的绑定的外部内容之后。
ngAfterViewInit:Angular 创建组件的视图后。
ngAfterViewChecked:在Angular 检查组件视图的绑定之后
复制代码

Angular 中路由的工作原理

Angular 应用程序具有路由器服务的单个实例,并且每当URL 改变时,相应的路由就与路 由配置数组 进行匹配。在成功匹配时,它会应用重定向,此时路由器会构建ActivatedRoute 对象的树, 同时包含路由器的当前状态。在重定向之前,路由器将通过运行保护(CanActivate) 来检查是否允许新的状态。Route Guard 只是路由器运行来检查路由授权的接口方法。 保护运行后,它将解析路由数据并通过将所需的组件实例化到 <router-outlet></router-outlet> 来激活路由器状态。

解释rjx 在Angular 中的使用场景

Rxjs 是在微软所提供的一种的异步处理数据的方式,在Angular 中处理网络通信时用到了。
创建一个Observable 并subsribe
比如:this.http.get('').subscribe((data)=>{ })
复制代码

到这里基本完成了还有哪里没有讲述到的欢迎各位大佬指正批评。建议点赞+收藏

基础篇链接点击跳转

文章分类
前端
文章标签