webpack作为一个模块打包器(module bundler),webpack视HTML,JS,CSS,图片等文件都是一种资源,每个资源文件都是一个module文件,webpack就是根据每个module文件之间的依赖关系将所有的module打包(bundle)起来。本文将介绍自己平时开发过程对webpack的一些实践。
ps.本人webpack的实践是配合Vue框架进行开发,故以下介绍有少部分内容是基于Vue框架进行说明,不过原理也是适用于其他框架的。
webpack基本配置组成
webpack的配置在实际开发中一般以独立的文件存在,每个配置文件都是JavaScript 文件导出的一个对象,公共的配置部分为webpack.base.conf.js
,再根据开发(dev)和生产(prod)分webpack.dev.conf.js
和webpack.prod.conf.js
。一个成熟的webpack.base.conf.js
里导出的对象包含:
- entry:入口文件
- output:bundle后输出文件,包括输出文件的文件名、路径等
- resolve:模块解析,一般是是设置模块的解析路径、文件别名等
- module:解析不同类型模块的loader,如url-loader、eslint-loader等
- plugins:定制webpack构建过程的插件或者自定义插件,如LoaderOptionsPlugin等
使用resolve.alias别名替代完整路径
webpack把一切模块视为资源,因此在每个模块中,我们可以使用通过导入模块使用其他模块,webpack支持CommonJs的require()
和ES6的import
等常见的模块导入方式,下面是导入一个公共文件夹(_image)图片的示例:
var img = require('../../_image/webpack.png')
正常情况下需要使用相对路径导入,也就是你需要一级一级目录的往上找出当前模块的路径,如果文件路径比较深,那么require的路径中就会出现很多../
,代码就因此显得不优雅,因为这个_image
文件夹是放在项目最外层的公共文件夹,如果以项目最外层开始索引,那么require下的路径就只是_image/webpack.png
,省去许多../
,我们可以通过webpack的resolve.alias
给路径设置别名:
var path = require('path')
module.exports = {
...
resolve: {
alias: { // 设置路径别名
'_image': path.resolve(__dirname, './_image'),
'styles': path.resolve(__dirname, './src/_styles')
}
}
...
}
在任意位置引用_image
文件夹时如下:
var img = require('_image/webpack.png')
如果是在CSS中使用路径别名,需要在别名前加上~
,以下是在stylus
中import
公共变量文件variables.styl
:
<style scoped lang="stylus">
/* 设置styles的别名后要在别名前加上~ */
@import "~styles/variables";
.demo { color: _grey; }
</style>
使用resolve.extensions添加扩展名
引用其他文件时需要注明该文件的类型,如注册一个全局modal组件需要注明文件扩展名:
import Vue
Vue.component('modal', require('src/_components/ui/modal/modal.vue'));
在webpack配置resolve.extensions
将常见的文件名加上后,加不需要每次都加上文件扩展名了:
webpack.base.conf.js
module.exports = {
...
extensions: ['.js', '.vue', '.json']
...
}
代码分割
代码分割(Code Splitting)是webpack的强大功能之一,代码分割可以将第三方库代码(vendor)和业务代码分开打包,在使用到对应的逻辑的时候才引入相应的代码,从而使得页面加载的js文件体积大大降低,提升页面性能,关于webpack的代码分割具体可以参考Webpack 大法之 Code Splitting。代码分割的关键点在于找到代码的分割点,在Vue框架开发的SPA中,我们可以将每一个路由视为一个分割点,在进入到该路由的时候才会加载该页面所需的js文件。具体的做法是在每个路由上使用resolve来动态加载所需JS:
// src/_router/index.js
import Router from 'vue-router';
export default new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/page',
name: 'page_name',
component: function (resolve) { // 通过异步加载
require(['../page/page.vue'], resolve)
}
}
]
});
LoaderOptionsPlugin设置全局变量
默认情况下,webpack不会将任何module暴露给全局,如果需要使用全局模块(如jQuery或全局CSS变量),需要通过plugins进行实现,例如有全局的stylus变量模块variables.styl
,通过webpack内置的LoaderOptionsPlugin可以将这个模块暴露给全局:
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
stylus: {
import: [
path.resolve(__dirname, './src/_styles/variables.styl')
]
}
}
})
]
这样,在每个组件中使用到全局CSS变量时,不需再使用@import "~styles/variables";
即可直接使用变量。
(完)