前面写过。为了更好的解释,我们模仿Vue.js开发了一个类似简化版的前端框架快报(文档)来帮助大家理解一些细节。所以在开始之前,我们先对这个项目的结构有个大概的了解,方便后续的描述。温馨提示:建议大家在开始之前先把项目clone到Github上,然后从源码中学习!目录结构其实只需要关注以下四个文件夹即可:src:框架源代码;loader:类似于vue-loader,用于解析.paper文件的loader;style-loader:同上,但是这个是用来解析Style文件的(包括.css文件和.paper文件中的style标签);loader-plug:一些辅助功能,比如校对webpack的一些配置。框架源码接下来我们展开源码src部分的目录结构(因为我们这里的重点不是源码部分,而是那些loader或者plug是如何配置完成一系列解析任务的,所以源码部分只是下面的描述将到此为止)。core:框架对象的基本代码global-api:挂载框架对象的全局方法instance:框架对象index.js:框架对象运行入口init.js:负责对象的初始化及相关工作生命周期.js:负责对象的生命周期管理render.js:对象渲染启动等任务observe:监听数据变化方法(框架对象使用)vnode:虚拟DOM相关代码(框架对象使用)module:extendbuilt-ininstructions,components,etc.forframeobjectstools:一些工具和方法是写在一起的,因为可以重用,方便管理。index.js:包入口文件,也就是这个文件将所有的资源整合成一个完整的框架,所以从上面的代码可以看出,文件src/core/instance/index.js就是对象本身,从这个文件!有什么不明白的可以去issue给我们留言。Loader和执行顺序对于我们学习使用的项目QuickPaper,我们将代码整合到文件.paper中,文件结构大致如下:你想,我们用webpack打包项目的时候,他是不可能知道.paper文件的,当然不可能知道如何解析上面的文件,开发一个解析上面文件的loader就是这里要说明的。loader在讲解loader之前,我们需要先看看我们编辑的.paper是如何被我们使用的。因为我们如何使用它决定了我们需要如何解析它。与vue类似,假设我们有一个App.paper文件:importAppfrom'./App.paper';newQuickPaper({render:createElement=>createElement(App),//...});因为在render中只记录了页面内容,而.paper文件记录了页面内容+逻辑控制+页面样式。剩下的内容呢?//importjs[逻辑控制]importscriptfrom'./${filename}?QuickPaper&type=script&lang=js&hash=${id}&';//importcss[页面样式]import'./${filename}?QuickPaper&type=style&lang=css&hash=${id}&';script.render=${code};//[页面内容]exportdefaultscript;可以看出,页面内容默认是直接导出的,然后交给render配置项,其他的因为在内容中添加了新的import语句,所以会触发相应的loader进行解析。也就是说可以分为两步:第一步:对没有考虑过的内容执行新的import语句,触发对应的loader进行解析。第二步:导出render需要的contentstyle-loader,比如页面样式部分的import语句:import'./${filename}?QuickPaper&type=style&lang=css&hash=${id}&';我们怎么让webpack知道这是一个Style文件,是用css还是scss或者其他loader来解析,这是插件需要说明的部分。在此之前,我们需要解释一下样式加载器是如何工作的。为什么样式加载器很特别?根据返回值的类型,loader可以分为两种:一种是返回js代码的loader(也就是一个module的代码,有类似module.export的语句),另一种是其他loader不能用作最左边的加载器(例如返回CSS字符串)。让我们看看如何在我们的webpack中配置css加载器:{test:/\.css$/,loader:['quick-paper/style-loader/index.js','css-loader','postcss-loader']}这里重点是css-loader,属于第一种,返回js代码的loader。对于我们自定义的'quick-paper/style-loader/index.js',如果loader按照从右到左的顺序执行,很难得到真正的css代码。执行顺序(loader和picth)在讲解如何解决前面的问题之前,我们需要先解释一下loader的picth和执行顺序。比如上面配置的三个loader,执行顺序分为Pitchphase和Normalphase(可以理解为loader本身的行为):Pitchphase:'quick-paper/style-loader/index.js'->'css-loader'->'postcss-loader'正常阶段:'postcss-loader'->'css-loader'->'quick-paper/style-loader/index.js'有一个特性在Pitch阶段,如果一个loader有返回值,就会停止后续执行。提示:停止执行意味着右边的loader包括它自己已经执行完毕(Pitch阶段和Normal阶段都结束了),返回值会返回给之前的loader(Normal阶段)!如何实现?在这里,我们可以通过设置一个带有“quick-paper/style-loader/index.js”返回值的Pitch来实现这一点。看代码结构://quick-paper/style-loader/index.jsconstloaderApi=()=>{};loaderApi.pitch=function(remainingRequest){//request=""!!../../node_modules/css-loader/dist/cjs.js!../../node_modules/postcss-loader/src/in...letrequest=loaderUtils.stringifyRequest(this,'!!'+remainingRequest)return`//获取真正的css内容varcontent=require('+request+');//然后调用添加到页面的方法生效require('./addStylesClient.js')(content);`;};module.exports=loaderApi;我们在“quick-paper/style-loader/index.js”中定义了Picth方法。在这个方法中,返回一个js字符串。该字符串将在项目运行时运行。意思是调用样式加载器获取到真正的css后,运行addStylesClient.js方法使其在页面生效。温馨提示:addStylesClient.js方法请直接查看项目源码,简单易懂。给样式加上一个hash值让作用域生效就是在这个方法中。插件的功能和一些技巧在这里解释。一个.paper文件拆分后,如何让对应的loader解析。首先需要了解插件的执行时机。什么是插件?你可以这样理解:如果说loader帮助webpack解析更多类型的文件,那么插件就是个杂工。前者有专门的分工,后者在特殊情况下提供帮助,而不是针对某个文件。比如每次打包前可以调用一个函数查看和删除上次打包的结果,或者打包失败时重新设置一些参数,或者其他一些操作。如何实现?那么,我们在这里需要插件做什么?别忘了我们的需求是(以css为例),如果遇到类似import'./${filename}?QuickPaper&type=style&lang=css&hash=${id}&';的导入语句,我们的工具lang=css发现是样式文件,应该交给专门解析css的loader。当然我们可以主动修改webpack的配置:']}不过为了简单起见,我们可以通过插件在每次打包前修改loader配置(当然也包括js等相关项),这样,就实现了。
