??对于 Mustache
模板的自动解析和加载,网上有开源的 mustache-loader
实现,但其关注度实在太低,而 html-loader
足以达到所需功能:
-
加载 .mst 文件,并压缩内容;
-
将文件中 img:src
等相对路径属性自动替换为绝对目标地址;
??对于其他模板语言同样可以使用这种方法,就可以在项目中灵活的使用不同的模板库了。不过需要注意的是,同一个项目中最好只使用一种模板语言,方便管理,同时不会增加打包文件大小。
??将 .mst 模板加载到页面中和 .less 方法差不多。在对应的 .js 文件中显式引入,然后用 extractTemplate
方法提取出模板内容即可:
?这种显式引入的方式有一个好处是,可以手动控制不同的模板和样式。在实际产品需求中,内容和样式改变是很频繁的,而功能逻辑的变化相对要慢一些,这样通过 js 引用不同版本的模板和样式就会比较灵活。如果能把这一套管理机制抽象出来单独进行配置也是很不错的。
??页面文件在 Webpack 中也是以模板的角色存在的,解析方式和模板一样,规则见上文。由于是页面入口文件,在 Webpack 中还需要使用 HtmlWebpackPlugin
插件进行配置。如下配置中,项目存在两个不同的页面入口,所以需要两个 HtmlWebpackPlugin
实例:
由于用户每次进入 Web 页面都会加载首页,所以首页越小越节省流量。参考 Vue 项目的 index.html
就会发现里面基本只有一个骨架,具体内容都在组件中。但项目配置本身不会对这点进行假设,所以即使在首页中写入所有内容也是可行的。
打包
??项目的主要打包配置前文已经介绍差不多了,其他具体配置参看官方文档即可。采用该项目结构的最后打包结果,所有部署文件包括图片加起来没有超过 130K。在浏览器中,因为 gzip 的原因,全页面加载网络流量不到 70K。
数据接入层
??前文已经提到过,把数据请求单独作为一个层主要是为了分离出复杂多变的数据请求接口,还有一个好处是接口 mock 数据也可以在这里统一处理。
接口封装
??一个项目中可能在很多地方都会请求同一个接口。对于单个接口请求,可能有不同方法,比如用 ajax、fetch、jsonp、axios 甚至 jQuery 库;有的是 GET,有的是 POST;有的还需要带 cookie,其他却不需要;返回数据的格式也许还不是统一的。而 java script 逻辑只关心输入和输出,把这些请求细节都放在另外一个地方单独维护,会使主要业务逻辑更加简洁。在项目中使用时,只需要以 Promise
的形式调用方法即可。接口封装的示例代码如下:
封装 mock 数据
??前后端协同开发时,需要首先定好接口,给出 mock 数据示例。所以在 DAL
层把 mock 数据封装好,会节省很多工作。在项目中会将 mock 数据直接保存成 .json 格式的文件,然后在 DAL 入口文件中通过 import
导入,再使用一个工厂方法来对外提供 mock
方法,即可使用 mock 数据了。下面是入口文件中相关代码:
通过 DAL 使用接口
??有了 DAL
层对各请求接口的聚合,在其他地方使用就比较简单了,直接上代码:
组件化开发
小型 Web 页面的组件和 .vue
文件结构类似,只是分成了三个文件:
-
样式。内容和使用方式是基本一样的;
-
模板。后者 Vue
有自己的模板语法,前者则用的 Mustache
,也可支持其他模板。如果 Vue
的模板加载器单独分离出来,那理论上也是可以拿过来使用的;
-
控制逻辑。JS 逻辑部分则有些不一样,Vue
框架有着自己独特完美的双向绑定机制,其接口和生命周期也是围绕它来设计的(这里只针对 .vue
文件进行讨论,类 React
使用方式很大程度是为了方便拉取用户而设定)。小型 Web 页面因为简单,所以重心都放在了组件初始化和渲染上;
组件在小型 Web 页面中定位是很明确的,即只针对页面呈现和交互,所以对外接口的设计也不复杂。如果组件采用的是 MVC 模式,那就很难讨论,因为 Controller 本身就是“老大”,可能有很多行为。Presenter 和 ViewModel 则相对简单,它们的区别只是内在机制不同,对外是行为是差不多的。这里不考虑大型 Web 页面,小型 Web 页中组件的接口默认就两种:接受纯数据参数(props
);对外公布事件接口。相比于更高级的 Vue,少了一个 Slot
插槽功能。
使用组件
??使用组件的方式很直接,看代码:
组件中一个 init
方法并不能搞定全部需求,因为项目中 init
方法不仅包含了组件渲染逻辑,还有事件绑定逻辑。当组件数据内容更新时,还需要抽取出一个 render
或 update
方法单独进行调用来更新界面。这不像 Vue 自带双向数据绑定神器,所以要麻烦点。
??使用组件提供的事件也很简单,代码如下:
这里事件句柄的参数采用了 (Object data, Event e)
的形式。其中 data
表示事件来源,它可以是被点击对象的 ViewModel
,或者简单点,直接是被点击对象所代表的的原始数据;e
则是 HTML 的事件参数。
组件的参数处理和渲染
??组件内部绑定到具体的模板前文已经示例说明过了。在渲染组件内容时,还需要处理参数内容,并将其渲染到页面指定地方。这里直接上代码:
在构造器中,首先定义 props
参数的格式,并给上默认值。在 init
方法中,则将 data 中的参数赋值给 props
,这里一般是会有数据转化处理逻辑。
??最后直接进行组件渲染。可以发现,如果想要使用其他模板引擎,是很容易替换的。如果采用 SSR 服务端渲染组件,那可以各种模板库全放进来,一个工厂方法就可以进行自动化处理。
??组件的参数被取名为 props
,完全是仿造 Vue
/React
。因为它们的功能和定位基本是一样的,而且官方推荐的最佳实践这里也基本都推荐。具体这样做的几点思路如下:
-
小项目做不到 Vue
/React
的参数验证功能,但显式表示 props
参数有自描述文档的作用,需要哪些参数及其类型一目了然;
-
构造