最近,一直尝试使用webpack做多页面应用的开发。并且一个实际的项目为原型,实现对一个静态的企业站进行优化。原站点地址,测试站点地址。
如果想要做一个自己个个人博客,或者企业官网来说,有一定的参考意义。喜欢的同学,请不要吝啬star一下。
webpack + pug
项目中使用了webpack进行构建,使用pug编译器可以减少开发过程中的html代码。
webpack的resolve.alias
在做模块化开发的过程中,有一个需要解决的问题就是引用模块的路径问题。
注:在webpack中,每一个文件(不管是js,css, html,还是图片等)都被称之为一个块。
为了实现模块化,细粒度化的控制,往往会将代码块分成为不可分割的块,这样做虽然方便了管理控制,但是也会造成项目的文件嵌套很严重,再饮用的时候需要格外小心路径,同时也会造成开发者的负担(抛开其他不讲,但从技术角度来说,对于开发人员来说,能用一行代码解决的问题,绝不用两行,能少输入一个单词就少输入一个)。
而webpack的resolve.alias
可以为指定路径的字符串起别名。在本文所使用的示例,这样定义别名:
...
resolve: {
alias: {
'@': path.join(__dirname, '..', 'pages/'), // 根目录
'@css': path.join(__dirname, 'assets/css/'), // css
'@img': path.join(__dirname, 'assets/imgs/'), // picture
// '@font': path.join(__dirname, 'assets/fonts/'),
'@data': path.join(__dirname, 'pages/data/'), // mock data
'@utils': path.join(__dirname, 'pages/utils/') // snippets code
}
},
...
当然,上面的别名并不是万能的,有一个问题就是background-iamge
和 font-face
的使用url()
会有一些问题,url()
中的路径必须是字符串,暂时没有好的办法解决。
但是使用sass
,可以定义变量,可以通过变量来指定路径,但是要严格控制引用变量模块的文件的目录,在本文所使用的示例中,统一将应用变量文件assets/css/path.scss
的文件,控制在两个层级。具体可参考所提供源代码中的具体使用。
模拟数据
实际的项目没有使用任何一种语言的后端代码,更不用说数据库。全部使用的是模拟数据。
为了方便管理维护项目的模拟数据,将项目的所有数据统一整理到了示例的pages/data
目录下。
静态资源图片的处理
第一优化的时候,就简单的讲了下,如何使用imagemin提供的插件,来实现对常见类型(.jpg,.png,*.gif)图片的处理。
第一种引用图片的方案
之前做单页面应用开发的时候,喜欢将所有的图片优化处理后统一放在一个目录中,然后将它们放在服务器中,最后在开发或生产环境中,使用绝对路径进行访问。
这种方式的好处是不用担心相对路径造成的路径问题。但是缺点是,操作起来不方便,尤其是开发环境。因为你不知道项目究竟要使用多少的静态资源,尤其是使用哪种静态资源。
这种方式在团队合作的项目中,比较常见,但是对于提升团队的效率并不明显。
第二种引用图片的方案
所以,对于开发者来说,如果如果需要什么静态资源,就放在自己的本地目录,这样可以随心所欲的添加。
在本文所采用的示例中,我做了一些尝试,将所有的图片资源进行了分类。
- 需要转化为base64的图片放一个文件夹
assets/imgs/base64/
; - 需要合成雪碧图的单独放在一个文件夹,
assets/imgs/sprites/
,为了方便管理合成不同雪碧图的源图片,我又在该目录下创建了子文件夹; - 而对于
<img src="..." />
要引用的图片的存放使用了两个文件夹,assets/imgs/static
存放了未经优化的所有的图片,而目录assets/imgs/others
,存放了所有优化过的图片(包含两部分,一部分是使用npm run img
命令优化的assets/imgs/static
目录下的图片,另一部分是npm run dev
命令优化的雪碧图图片,它的前缀带有*-sprite
这样的后缀)。
这种方案,使用的是相对路径应用图片。可参考pages/data/contactus.js
文件的代码:
const loadImg = require('@utils/load-img')
module.exports = {
cn_name: '联系我们',
en_name: 'CONTACT US',
img: loadImg('second/contactus-tag.png'),
...
}
而工具代码片段loadImg的代码如下:
module.exports = function(str) {
return require('@img/other/' + str)
}
模拟类似vue单页面的路由跳转
很明显的一点是传统的多页面应用的业务模块往往会出现多个页面之间会有很多相同内容,这样在单击导航实现路由切换的时候,总是会看到相同的内容,这样会给用户造成一种错觉‘为什么总是同一个页面’。这样的用户体验往往不好。最突出的就是包含二级导航的页面。(可参考圣捷集团的官网)
单页面应用给我们了一个很好的启示,可以通过将这些页面结构相似的,而只有一部分内容类同的页面组合成为一个页面。
这样做的好处显而易见,减少多页面构建生成的页面数量,我们之构建生成一级和二级页面,以及一些页面结构很少雷同的页面,而不构建生成三级页面。
优化后的示例地址
具体的实现有两种解决方案。
第一种,在每个页面中使用一个vue(结合vue-router)的示例(也可以使用,react,angular)。
第二种,自己实现对不同hash
的处理。
注:如果使用hash,在开发的时候一定要模拟一个服务器环境,直接用浏览器打开是无法实现的,浏览器控制台会提示跨域的错误。
本文所使用的示例用的是第二种方案。
具体的实现过程如下:
在生成子导航的模拟数据中添加了一个type
值。
tabs: [
{
cn_name: '圣捷投资',
en_name: 'SHENGJIE INVESTMENT',
type: 'investment'
}, {
cn_name: '董事长致辞',
en_name: 'CHAIRMANS SPEECH',
type: 'speech'
}
...
]
这样使用pug-loader
处理生成的html对应的元素中会包含一个data-type
自定义属性。参考代码如下:
<div class="tabs">
<div class="tab-item active" data-type="finance">
<div>互联网金融</div>
<div>ONLINE FINANCE</div>
</div>
<div class="tab-item" data-type="allfinance">
<div>全品类金融</div>
<div>WHOLE CATEGORY FINANCE</div>
</div>
...
</div>
然后,使用JavaScript通过控制触发条件,如url的hash改变,进而控制页面的展示效果。参考代码如下:
···
$('.tab-item').on('click', function() {
var type = $(this).data('type')
window.location.hash = type
tab(this, type)
})
···
首先,单点击二级导航时,改变url的hash。这样做可以让用户通过操作浏览器的前进和后退
按钮来控制页面,此外使用浏览器的前进和后退按钮
的好处是,浏览可以记录页面的状态。(只用上面的代码无法实现想要的效果)
其次,使用hashchange
这个浏览器自带的监听hash改变的api(他兼容>=ie8的浏览器,所以可以放心使用)。
···
$(window).on('hashchange', function() {
tabcheck()
})
···
通过它们,就可以轻松的实现url的hash
。
为了页面呈现更好的效果,可以给页面添加一个滚动的动画
,如果不使用hash
在传统的页面中实现有些棘手。
那么针对页面底部的网站导航,如何结合hash
来操作页面并实现一致的路由切换效果呢?
这里需要监听页面的load
状态,在webpack中,使用commonjs
来组织js代码块,需要注意window.load(...)
无效的情况。
具体的实现就不一一介绍了,可参考demo中tabs.js文件的代码