当前位置: 首页 > Web前端 > HTML

20个前端灵魂拷问,彻底看懂你是中级前端工程师【下篇】

时间:2023-04-02 12:53:38 HTML

前端20个灵魂拷问,彻底看懂你是中级前端工程师。感觉大家比较喜欢看这类文章,以后还会多一些。欢迎加入我们第二个前端交流群。目前二群人很多,所以二群开放啦~欢迎各位小姐姐一起加入~~(包括师傅小姐姐)我的微信号在最后。需要了解底层实现原理,借鉴他们的思路实现业务需求,实现性能优化,在这些知识的基础上学习新的东西~事半功倍为什么我会把这些问题放在中间,这篇文章会在介绍完这些问题后,后面会给出原因。问题是1.为什么要模块化,各种模块化标准的移动端React开源项目。从头开始搭建webpack脚手架,前端模块化是不可避免的,而且非常复杂。应用程序的所有内容不可能都在一个文件里~模块化过程:传统命令空间代码实现:index.js(function(w){w.a=1})(window)原理:全局对象window下,挂载该属性,则可以全局获取该属性的值。原则上一个js文件作为一个模块就是一个IIFE函数->require.js是基于AMD规范的。AMD规范采用异步方式加载模块,模块的加载不影响后面语句的执行。所有依赖该模块的语句都定义在一个回调函数中,回调函数只有在加载完成后才会运行。这里介绍使用require.js实现AMD规范的模块化:使用require.config()指定引用路径等,使用define()定义模块,使用require()加载模块。代码实现://简单的对象定义define({color:"black",size:"unisize"});//当你需要一些逻辑来做准备工作时,你可以这样定义:define(function(){//这里可以做一些准备工作return{color:"black",size:"unisize"}});//依赖一些模块定义自己的模块define(["./cart","./inventory"],function(cart,inventory){//通过返回对象定义自己的模块return{color:"blue",size:"large",addToCart:function(){inventory.decrement(this);cart.添加(这个);}}});->sea.js基于CMD规范。CMD是另一种js模块化解决方案。它与AMD非常相似。靠就近,延迟执行。这个规范其实是sea.js在推广的时候产生的。代码实现:define(function(require,exports,module){var$=require('jquery');exports.sayHello=function(){$('#hello').toggle('slow');};});seajs.config({别名:{'jquery':'http://modules.seajs.org/jquery/1.7.2/jquery.js'}});seajs.use(['./hello','jquery'],function(hello,$){$('#beautiful-sea').click(hello.sayHello);});原理:在最上面导入sea.js的源码文件,在运行时转换代码,在开头指定入口文件,根据入口文件定义的数组(或者导入的数组)继续查找对应的依赖依赖)。->commonJsNode.js原生环境支持commonJs模块化规范。首先简单实现一个require:functionrequire(/*...*/){constmodule={exports:{}};((module,exports)=>{//模块代码在这里。在这个例子中,定义一个函数。//此时,exports不再是module.exports的快捷方式,//这个模块仍然会导出一个空的default对象。//当代码运行到这里时,exports不再是对module.exports的引用,//当前模块仍将导出一个Empty对象(就像上面声明的默认对象)module.exports=someFunc;//此时,模块现在将导出someFunc,而不是//默认对象。//当代码运行到这一点时,当前模块将导出someFunc而不是默认对象})(module,module.exports);returnmodule.exports;}require相当于将引用的模块复制到当前模块export和module.exports中,暴露接口export与module.exports的区别:export是对module.exports的引用。作为一个引用,如果我们修改它的值,实际上是修改了它对应的引用对象的值。commonJS同步加载模块。在服务器端,模块文件存放在本地磁盘上,读取速度很快,这样做没有问题。但在浏览器端,限于网络原因,更合理的方案是使用异步加载。简单总结就是exports->{}<-module.exports同时指向一个对象->ES6模块化是目前最常用的模块化规范:ES6模块化规范原生浏览器环境和Node.js环境不识别,但要使用它,必须使用babel将其编译成浏览器或Node.js可以识别的代码。为了节省时间,会有自动一键打包编译代码的工具。-webpack.ES6最强大的部分,它不仅支持静态验证,可以同步和异步加载,而且统一了前后端的模块化规范。Node和传统前端都可以使用这套规范。ES6模块和CommonJS模块的区别CommonJS模块输出一个值的副本,而ES6模块输出一个值的引用(第一次require一个不同路径的文件,会在require.cache中保存一个缓存,并且下次直接从缓存中读取)CommonJS模块在运行时加载,ES6模块是编译时输出接口。CommonJS加载的是一个对象(即module.exports属性),只有在脚本运行完毕后才会生成。ES6模块不是对象。它的对外接口只是一个静态定义,会在静态代码分析阶段生成。这就是TypeScript支持静态类型检查的原因,因为它使用了ES6模块化的解决方案。特别提醒:现在Node也可以如果你使用ES6模块化方案,可以用experimental看commonJs的index.jsconsta=require('./test1.js');constfunc=require('./test2');a.a=2;console.log(a.a,'test1');func()test2.jsconsta=require('./test1')module.exports=function(){console.log(a.a,'test2')}test1.jsleta={a:1}module.exports=a运行nodeindex.js看ES6的输出结果//math.jsexportletval=1exportfunctionadd(){val++}//test.jsimport{val,add}from'./math.js'console.log(val)//1add()console.log(val)//2ReactVue框架实现基本原理和设计思路~设计思路和基本原理:1.由传统的直接DOM操作改为数据驱动的方式来间接代替我们操作DOM。2、每次数据发生变化需要重新渲染时,只对DOM有差异的那部分进行操作。--diff算法有一系列的生命周期对,实际上是一种约定,在代码执行顺序中给出特定函数名的一部分来执行。常见的diff算法是比较上次的虚拟dom和更新后的虚拟dom,然后对真实dom进行patch,或者直接比较真实dom和虚拟dom。从头开始编写React框架。本文附带源代码。从零开始,实现了一个React框架前端需要理解的通用算法和数据结构。常见的数据结构:栈、队列、树、图、数组、单链表、双链表、图等……冒泡排序比较相邻的两个元素,如果前一个大于后一个,则交换位置。最后一个元素应该是第一轮中最大的一个。按照步骤1的方法比较两个相邻的元素。此时由于最后一个元素已经是最大的,所以不需要再比较最后一个元素functionbubble_sort(arr){for(vari=0;iarr[j+1]){varswap=arr[j];arr[j]=arr[j+1];arr[j+1]=交换;}}}}vararr=[3,1,5,7,2,4,9,6,10,8];bubble_sort(arr);console.log(arr);快速排序js代码实现分析:快速排序是对冒泡排序的改进,数据在第一次排序时分为两部分,一部分比另一部分小。然后递归调用两边进行quicksort。functionquick_sort(arr){if(arr.length<=1){返回arr;}varpivotIndex=Math.floor(arr.length/2);varpivot=arr.splice(pivotIndex,1)[0];左变量=[];变种权=[];for(vari=0;i');//=>[Error:EISDIR:illegaloperationonadirectory,read]异步读取文件的全部内容:fs.readFile('path',(err,data)=>{if(err)throw错误;console.log(数据);});上面读取的数据都是缓冲区数据,Buffer类是一个全局变量,直接与二进制数据打交道。如果路径存在则返回true,否则返回false。:fs.existsSync(path)一般Node.js中的同步API都是以sync结尾的,没有的一般都是异步的。我们通常使用异步API。Node.js中有四种基本流类型:Writable-可写数据流(例如fs.createWriteStream())。可读-可以从中读取数据的流(例如fs.createReadStream())。双工-可读可写流(例如net.Socket)。转换-一个双工流,其数据可以在读写期间修改或转换(例如zlib.createDeflate())。Node.js写的静态资源服务器这是我自己写的一个静态资源服务器。Node中这些常用模块中有大量的Buffer操作,是成为全栈工程师的基础。越是复杂的应用程序,执行的二进制操作就越多,比如自己定义的即时通讯协议,需要从Buffer中一点一点切出数据。如果是prob协议,则必须反序列化。但原理大多相似,并且涉及音频和视频。使用Node.js作为中间件,在同构服务器端渲染单页应用,并进行转发请求等操作。为了解决单页应用的SEO问题,传统的SSR渲染是在服务端运行代码,然后传递字符串。表单被传递到前端进行渲染。现在的单页应用只是把一个空的HTML文件和很多js文件传给前端,拿到文件后动态生成页面。这就导致搜索引擎爬虫无法爬到网页上的信息,都存在同构。同构是将单页应用、React、Vue等框架编写的代码在服务端运行一次(不是全部),然后返回字符串给前端渲染。这时候搜索引擎就可以抓取到关键词了。前端根据服务器返回的字符串渲染生成页面后,由js文件接管后续逻辑。这是一套完整的同构React服务端渲染源码这是我的React服务端渲染源码客户端入口文件://client/index.jsimportReactfrom'react';importReactDomfrom'react-dom';从'react-router-dom'导入{BrowserRouter,Route};从'react-redux'导入{Provider};从'../containers/redux-file/store'导入{getClientStore};从'导入{renderRoutes}react-router-config'从'../Router'导入路由器;conststore=getClientStore();constApp=()=>{return({renderRoutes(routers)}}/BrowserRouter>);};ReactDom.hydrate(,document.getElementById('root'));同构入口代码://server/index.jsimportexpressfrom'express';import{render}from'../utils';import{serverStore}from'../containers/redux-文件/存储';constapp=express();app.use(express.static('public'));app.get('*',function(req,res){if(req.path==='/favicon.ico'){res.send();return;}conststore=serverStore();res.send(render(req,store));});constserver=app.listen(3000,()=>{varhost=server.address().address;varport=server.address().port;console.log(host,port);console.log('启动连接了');});render函数:importRoutesfrom'../Router';import{renderToString}from'react-dom/server';从'react-router-dom'导入{StaticRouter,Link,Route};从'react'导入React;从'react-redux'导入{Provider};从'react-router-config'导入{renderRoutes};导入路由器从'../Router';从'react-router-config'导入{matchRoutes};导出constrender=(req,store)=>{constmatchedRoutes=matchRoutes(routers,req.path);matchedRoutes.forEach(item=>{//如果这条路由对应的组件有loadData方法if(item.route.loadData){item.route.loadData(store);}});console.log(store.getState(),Date.now())constcontent=renderToString({renderRoutes(router)});看着眼花缭乱,其实是在服务器端运行代码,然后拼接成字符串给前端唯一特别的地方:服务器代码注水:客户端代码脱水:store.jsimportthunkfrom'redux-thunk';import{createStore,applyMiddleware}from'redux';importreducersfrom'./reducers';exportconstgetClientStore=()=>{//下面这行代码是代码脱水,createStore可以传入第二个参数,看源码理解constdefaultState=window.context?窗口.context.state:{};returncreateStore(reducers,defaultState,applyMiddleware(thunk));};exportconstserverStore=()=>{returncreateStore(reducers,applyMiddleware(thunk));};同构的秘密:1、代码现在运行在服务器上。2.将字符串和注水数据返回给前端。3、前端拿到字符串和注水数据后,进行脱水渲染,然后js文件接管。这时候就是单页应用的逻辑了~考虑了半天觉得应该写这5道题。接下来的5个问题将在下周更新。而算法是要有编写轻量级框架和性能优化的基础。使用Node.js是为整个栈的开发打下基础。同构是为高并发场景打下框架基础。设计思路,在写业务代码的时候,在考虑时间复杂度和空间的同时,还要考虑框架的底层实现。我觉得写的不错,给个star吧。欢迎加入我们的第二群~我的个人微信公众号:CALASFxiaotan