webpack多入口文件页面打包配置详解


Posted in Javascript onJanuary 09, 2018

大多数情况下,我们使用 webpack来打包单页应用程序,这个时候只需要配置一个入口,一个模板文件,但也不尽是如此,有时候也会碰到多页面的项目,而且以我的经验来看,这种情况出现的频率还不低,例如项目比较大,无法进行全局的把握,或者项目需要多次的更新迭代等,都适合做成多页面程序,这就涉及到了 webpack的多页面文件的打包配置问题。

手动配置

单页应用程序和多页应用程序的 webpack配置文件其实绝大部分都还是相同的,只不过多页的配置需要在单页配置的基础上顾及到多个页面罢了,loader、output、plugins这些基本都不需要改动,需要改动的一般都是入口文件 entry,如果你用到了 抽离css样式的插件 extract-text-webpack-plugin、自动模板插件  html-webpack-plugin的话,那么还需要对这两个插件进行额外的改写,大多数情况下,我们也都只需要改动这三个地方,所以本文就只简单说下这三个位置,如果在实际的项目中还有其他的地方需要改动,参照这三个位置即可。

示例的文件目录如下:

webpack多入口文件页面打包配置详解 

entry

单页应用程序的入口配置一般如下所示:

entry: resolve(__dirname, "src/home/index.js")

这个配置就是指定 webpack从 /src/home/index.js这个文件开始进入,进行一系列的打包编译过程。

如果是多页应用程序,则需要多个入口文件,例如:

entry: {
 home: resolve(__dirname, "src/home/index.js"),
 about: resolve(__dirname, "src/about/index.js")
}

这样,整个项目就有了两个入口 home和 about

extract-text-webpack-plugin

extract-text-webpack-plugin 插件主要是为了抽离css样式,防止将样式打包在 js中引起页面样式加载错乱的现象,单页程序中,一般这样使用此插件:

plugins: [
 new ExtractTextPlugin('style.[contenthash].css')
]

而到了多页程序,因为存在多个入口文件以及对应的多个页面,每个页面都有自己的 css样式,所以需要为每个页面各自配置一下:

plugins: [
 new ExtractTextPlugin('home/[name].[contenthash].css'),
 new ExtractTextPlugin('about/[name].[contenthash].css')
]

除此之外还需要注意一点,每个页面也只需要自己的 css样式,理论上把别的页面 css样式文件也打包到自己的页面中当然也是可以的,但显然是不合理的,这只会增加冗余代码,还可能会导致不可预测的样式覆盖等问题,所以需要对下面这种 loader配置进行修改:

{
  test: /\.css$/,
  loader: 'style!css!autoprefixer'
},
{
  test: /\.scss$/,
  loaders: [
   'style',
   'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
   'sass',
   'autoprefixer'
  ]
},

上面的配置会把所有编译出来的 css文件打包到同一个文件中,我们要做的就是把这些 css分离开,每个页面都有各自单独的 css样式文件:

// 为每个页面定义一个 ExtractTextPlugin
const homeExtractCss = new ExtractTextPlugin('home/[name].[contenthash].css')
const aboutExtractCss = new ExtractTextPlugin('about/[name].[contenthash].css')
// ...
module: {
  rules: [
   // 每个页面的 ExtractTextPlugin 只处理这个页面的样式文件
  {
    test: /src(\\|\/)home(\\|\/)css(\\|\/).*\.(css|scss)$/,
    use: homePageExtractCss.extract({
     fallback: 'style-loader',
     use: ['css-loader', 'postcss-loader', 'sass-loader']
    })
   },
   {
    test: /src(\\|\/)about(\\|\/)css(\\|\/).*\.(css|scss)$/,
    use: salePersonalCenterExtractCss.extract({
     fallback: 'style-loader',
     use: ['css-loader', 'postcss-loader', 'sass-loader']
    })
   }
 ]
}
// ...
// 每个页面都有各自的 ExtractTextPlugin,所以需要都声明一遍
plugins: [
  homeExtractCss,
  aboutExtractCss
]

html-webpack-plugin

html-webpack-plugin插件的使用,在单页应用程序和多页应用程序中的 webpack配置没什么区别

new HtmlWebpackPlugin({
  filename: 'home/home.html',
  template: 'src/home/html/index.html',
  inject: true,
  minify: {
    removeComments: true,
    collapseWhitespace: true
  }
 })
 new HtmlWebpackPlugin({
  filename: 'about/about.html',
  template: 'src/about/html/index.html',
  inject: true,
  minify: {
    removeComments: true,
    collapseWhitespace: true
  }
 })

有几个页面,就对每个页面进行上述配置即可。

自动配置

上述的配置代码已经可以满足多页面开发需求了,但是有一点似乎有些遗憾,那就是每增加一个页面,就需要更新一遍 entry、extract-text-webpack-plugin、HtmlWebpackPlugin的配置,虽然只是几行代码的问题,而且基本上都是复制粘贴没什么难度,但毕竟代码再少也需要过问,并且需要改的地方比较多,仓促之下可能还会遗漏,要是能一劳永逸,写一遍代码,无论以后增删页面都不需要过问就好了。

稍微观察下这个目录就可以发现,这个目录结构其实是很有规律的:

webpack多入口文件页面打包配置详解 

每个页面都是 src/目录下的一个文件夹,这个文件夹中有两个子目录,分别存放这个页面的模板 html,样式文件 css,还有一个入口文件 index.js

既然有规则,那么肯定是可以进行程序编码的,如果按照这种规则,每个页面都是 ./src下的一个目录,目录名即为页面名,并且这个目录中的结构也都是相同的,那么可以通过一个通用方法来获取所有的页面名称(例如 home、about),这个通用方法的一个示例如下:

function getEntry () {
 let globPath = 'src/**/html/*.html'
 // (\/|\\\\) 这种写法是为了兼容 windows和 mac系统目录路径的不同写法
 let pathDir = 'src(\/|\\\\)(.*?)(\/|\\\\)html'
 let files = glob.sync(globPath)
 let dirname, entries = []
 for (let i = 0; i < files.length; i++) {
  dirname = path.dirname(files[i])
  entries.push(dirname.replace(new RegExp('^' + pathDir), '$2'))
 }
 return entries
}

借助 glob这个库,遍历 .src/目录下具有这种规律 src/**/html/*.html的子目录,通过正则匹配出这个子目录的名称
获取到了所有的页面名称,下面就好办了。

entry

// entry: resolve(__dirname, "src/home/index.js")
// 改为
entry: addEntry()
//...
function addEntry () {
 let entryObj = {}
 getEntry().forEach(item => {
  entryObj[item] = resolve(__dirname, 'src', item, 'index.js')
 })
 return entryObj
}

extract-text-webpack-plugin

// plugins: [
 // new ExtractTextPlugin('home/[name].[contenthash].css'),
 // new ExtractTextPlugin('about/[name].[contenthash].css')
//]
// 改为
const pageExtractCssArray = []
getEntry().forEach(item => {
 pageExtractCssArray.push(new ExtractTextPlugin(item + '/[name].[contenthash].css'))
})
// ...
plugins: [...pageExtractCssArray]

module.rules样式相关的两个loaders删掉,改为动态添加:

getEntry().forEach((item, i) => {
 webpackconfig.module.rules.push({
  test: new RegExp('src' + '(\\\\|\/)' + item + '(\\\\|\/)' + 'css' + '(\\\\|\/)' + '.*\.(css|scss)$'),
  use: pageExtractCssArray[i].extract({
   fallback: 'style-loader',
   use: ['css-loader', 'postcss-loader', 'sass-loader']
  })
 })
})
// ...
module.exports = webpackconfig

html-webpack-plugin

plugins中无需手动初始化 html-webpack-plugin,改为动态添加:

getEntry().forEach(pathname => {
 let conf = {
  filename: path.join(pathname, pathname) + '.html',
  template: path.join(__dirname, 'src', pathname, 'html', 'index.html')
 }
 webpackconfig.plugins.push(new HtmlWebpackPlugin(conf))
})
// ...
module.exports = webpackconfig

完成了上述修改后,以后无论是在项目中添加页面还是删除页面,都无需再对  webpack配置进行手动修改了,虽然开始时开起来似乎这种动态的自动配置代码比较多,而且稍微复杂一点,但是从长期来看,绝对是一劳永逸的好做法。

另外,如果你的项目目录结构和我示例的目录结构不一样,那么就需要你根据自己的目录结构对代码进行少许的修改,但整体解决问题的方法是不变的,一个易于维护的项目,目录结构都该是有律可循的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
使用Jquery搭建最佳用户体验的登录页面之记住密码自动登录功能(含后台代码)
Jul 10 Javascript
Jquery右下角抖动、浮动 实例代码(兼容ie6、FF)
Aug 15 Javascript
解析jquery中的ajax缓存问题
Dec 19 Javascript
JavaScript中Cookie操作实例
Jan 09 Javascript
JS模拟Dialog弹出浮动框效果代码
Oct 16 Javascript
关于Javascript回调函数的一个妙用
Aug 29 Javascript
基于Vue2.0的分页组件
Mar 16 Javascript
JS实现移动端实时监听输入框变化的实例代码
Apr 12 Javascript
AngularJS实现与后台服务器进行交互的示例讲解
Aug 13 Javascript
validform表单验证的实现方法
Mar 08 Javascript
vue控制多行文字展开收起的实现示例
Oct 11 Javascript
小程序卡片切换效果组件wxCardSwiper的实现
Feb 13 Javascript
Vue项目组件化工程开发实践方案
Jan 09 #Javascript
详解webpack之scss和postcss-loader的配置
Jan 09 #Javascript
SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题的解决方法
Jan 09 #Javascript
详解Webpack+Babel+React开发环境的搭建的方法步骤
Jan 09 #Javascript
详解webpack babel的配置
Jan 09 #Javascript
webpack+vue2构建vue项目骨架的方法
Jan 09 #Javascript
webpack vue 项目打包生成的文件,资源文件报404问题的修复方法(总结篇)
Jan 09 #Javascript
You might like
PHP实现支持SSL连接的SMTP邮件发送类
2015/03/05 PHP
ThinkPHP、ZF2、Yaf、Laravel框架路由大比拼
2015/03/25 PHP
php 多文件上传的实现实例
2016/10/23 PHP
Laravel第三方包报class not found的解决方法
2019/10/13 PHP
laravel Task Scheduling(任务调度)在windows下的使用详解
2019/10/22 PHP
javascript 面向对象编程基础:继承
2009/08/21 Javascript
自己的js工具_Form 封装
2009/08/21 Javascript
jquery多选项卡效果实例代码(附效果图)
2013/03/23 Javascript
基于jquery扩展漂亮的下拉框可以二次修改
2013/11/19 Javascript
javascript十六进制及二进制转化的方法
2015/05/06 Javascript
js动态创建及移除div的方法
2015/06/03 Javascript
详解AngularJS中自定义指令的使用
2015/06/17 Javascript
深入学习JavaScript中的Rest参数和参数默认值
2015/07/28 Javascript
举例讲解jQuery中可见性过滤选择器的使用
2016/04/18 Javascript
indexedDB bootstrap angularjs之 MVC DOMO (应用示例)
2016/06/20 Javascript
详解小程序输入框闪烁及重影BUG解决方案
2018/08/31 Javascript
vue单页面应用打开新窗口显示跳转页面的实例
2018/09/21 Javascript
es6中reduce的基本使用方法
2019/09/10 Javascript
JS实现点击下拉列表文本框中出现对应的网址,点击跳转按钮实现跳转
2019/11/25 Javascript
JavaScript 中的无穷数(Infinity)详解
2020/02/13 Javascript
jQuery实现本地存储
2020/12/22 jQuery
python计算程序开始到程序结束的运行时间和程序运行的CPU时间
2013/11/28 Python
使用python开发vim插件及心得分享
2014/11/04 Python
Python max内置函数详细介绍
2016/11/17 Python
python机器学习实战之树回归详解
2017/12/20 Python
Python读取YUV文件,并显示的方法
2018/12/04 Python
python多进程读图提取特征存npy
2019/05/21 Python
Python pandas DataFrame操作的实现代码
2019/06/21 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
将tf.batch_matmul替换成tf.matmul的实现
2020/06/18 Python
python使用自定义钉钉机器人的示例代码
2020/06/24 Python
澳大利亚最受欢迎的美发和美容在线商店:Catwalk
2018/12/12 全球购物
公司承诺书怎么写
2014/05/24 职场文书
乡镇四风对照检查材料
2014/08/31 职场文书
法人单位授权委托书范文
2014/10/06 职场文书
2014年行政工作总结
2014/11/19 职场文书