最近在做项目的时候遇到了一个场景:一个项目有多个入口,不同的入口,路由、组件、资源等有重叠部分,也有各自不同的部分。由于不同入口下的路由页面有一些是重复的,因此我考虑使用 Webpack 多入口配置来解决这个需求。

再一次,在网上找的不少文章都不合我的需求,很多文章都是只简单介绍了生产环境下配置,没有介绍开发环境下的配置,有的也没有将多入口结合 vue-routervuexElementUI 等进行配置,因此在下通过不断探坑,然后将思路和配置过程记录下来,留给自己作为笔记,同时也分享给大家,希望可以帮助到有同样需求的同学们~

1. 目标分析

  1. 一个项目中保存了多个 HTML 模版,不同的模版有不同的入口,并且有各自的 router、store 等;

  2. 不仅可以打包出不同 HTML,而且开发的时候也可以顺利进行调试;

  3. 不同入口的文件可以引用同一份组件、图片等资源,也可以引用不同的资源;

代码仓库:multi-entry-vue

示意图如下:

2. 准备工作

首先我们 vue init webpack multi-entry-vue 使用 vue-cli 创建一个 webpack 模版的项。文件结构如下:

.

├── build

├── config

├── src

│   ├── assets

│   │   └── logo.png

│   ├── components

│   │   └── HelloWorld.vue

│   ├── router

│   │   └── index.js

│   ├── App.vue

│   └── main.js

├── static

├── README.md

├── index.html

├── package-lock.json

└── package.json

这里顺便介绍在不同系统下生成目录树的方法:

  1. mac 系统命令行生成目录树的方法 tree-I node_modules--dirsfirst ,这个命令的意思是,不显示 node_modules 路径的文件,并且以文件夹在前的排序方式生成目录树。如果报没有找到 tree 命令的错,安装 tree 命令行 brew install tree 即可。

  2. windows 系统在目标目录下使用 tree/f1.txt 即可把当前目录树生成到一个新文件 1.txt 中。

首先我们简单介绍一下 Webpack 的相关配置项,这些配置项根据使用的 Webpack 模版不同,一般存放在 webpack.config.jswebpack.base.conf.js 中:

const path = require('path')

module.exports = {

context: path.resolve(__dirname, '../'),

entry: {

app: './src/main.js'

},

output: {

path: path.resolve(__dirname, '../dist'),

filename: 'output-file.js',

publicPath: '/'

},

module: {}, // 文件的解析 loader 配置

plugins: [], // 插件,根据需要配置各种插件

devServer: {} // 配置 dev 服务功能

}

这个配置的意思是,进行 Webpack 后,会在命令的执行目录下新建 dist 目录(如果需要的话),并将打包 src 目录下的 main.js 和它的依赖,生成 output-file.js 放在 dist 目录中。

下面稍微解释一下相关配置项:

  1. entry: 入口文件配置项,可以为字符串、对象、数组。以上面的对象形式为例, app 是入口名称,如果 output.filename 中有 [name] 的话,就会被替换成 app

  2. context: 是 webpack 编译时的基础目录,用于解析 entry 选项的基础目录(绝对路径), entry 入口起点会相对于此目录查找,相当于公共目录,下面所有的目录都在这个公共目录下面。

  3. output: 出口文件的配置项。

  4. output/path: 打包文件输出的目录,比如上面的 dist,那么就会将输出的文件放在当前目录同级目录的 dist 文件夹下,没有这个文件夹就新建一个。可以配置为 path.resolve(__dirname,'./dist/${Date.now()}/') (md 语法不方便改成模板字符串,请自行修改)方便做持续集成。

  5. output.filename: 输出的文件名称, [name] 的意为根据入口文件的名称,打包成相同的名称,有几个入口,就可以打包出几个文件。比如入口的 key 为 app,打包出来就是 app.js,入口是 my-entry,打包出来就是 my-entry.js

  6. output.publicPath: 静态资源的公共路径,可以记住这个公式: 静态资源最终访问路径=output.publicPath+资源loader或插件等配置路径。举个例子, publicPath 配置为 /dist/,图片的 url-loader 配置项为 name:'img/[name].[ext]' ,那么最终打包出来文件中图片的引用路径为 output.publicPath+'img/[name].[ext]'='/dist/img/[name].[ext]'

本文由于是入口和出口相关的配置,所以内容主要围绕着 entryoutput 和一个重要的 webpack 插件 html-webpack-plugin,这个插件是跟打包出来的 HTML 文件密切相关,主要有下面几个作用:

  1. 根据模版生成 HTML 文件;

  2. 给生成的 HTML 文件引入外部资源比如 link、 script 等;

  3. 改变每次引入的外部文件的 Hash,防止 HTML 引用缓存中的过时资源;

下面我们从头一步步配置一个多入口项目。

3. 开始配置

3.1 文件结构改动

src 目录下将 main.jsApp.vue 两个文件各复制一下,作为不同入口,文件结构变为:

.

├── build

│   ├── build.js

│   ├── check-versions.js

│   ├── logo.png

│   ├── utils.js

│   ├── vue-loader.conf.js

│   ├── webpack.base.conf.js

│   ├── webpack.dev.conf.js # 主要配置目标

│   └── webpack.prod.conf.js # 主要配置目标

├── config

│   ├── dev.env.js

│   ├── index.js

│   └── prod.env.js

├── src

│   ├── assets

│   │   └── logo.png

│   ├── components

│   │   └── HelloWorld.vue

│   ├── router

│   │   └── index.js

│   ├── App.vue

│   ├── App2.vue # 新增的入口

│   ├── main.js

│   └── main2.js # 新增的入口

├── static

├── README.md

├── index.html

└── package.json

3.2 简单配置

要想从不同入口,打包出不同 HTML,我们可以改变一下 entryoutput 两个配置,

// build/webpack.prod.conf.js

module.exports = {

entry: {

entry1: './src/main.js',

entry2: './src/main2.js'

},

output: {

filename: '[name].js',

publicPath: '/'

},

plugins: [

new HtmlWebpackPlugin({

template: "index.html", // 要打包输出哪个文件,可以使用相对路径

filename: "index.html" // 打包输出后该html文件的名称

})

]

}

根据上面一小节我们知道,webpack 配置里的 output.filename 如果有 [name] 意为根据入口文件的名称,打包成对应名称的 JS 文件,那么现在我们是可以根据两个入口打包出 entry.jsentry2.js

打包的结果如下:

当前代码:Github - multi-entry-vue1

如上图,此时我们 npm run build 打包出一个引用了这两个文件的 index.html,那么如何打包出不同 HTML 文件,分别应用不同入口 JS 文件呢,此时我们需要借助于 HtmlWebpackPlugin 这个插件。

HtmlWebpackPlugin 这个插件, new 一个,就打包一个 HTML 页面,所以我们在 plugins 配置里 new 两个,就能打包出两个页面来。

3.3 打包出不同的 HTML 页面

我们把配置文件改成下面这样:

// build/webpack.prod.conf.js

module.exports = {

entry: {

entry: './src/main.js', // 打包输出的chunk名为entry

entry2: './src/main2.js' // 打包输出的chunk名为entry2

},

output: {

filename: '[name].js',

publicPath: '/'

},

plugins: [

new HtmlWebpackPlugin({

filename: 'entry.html', // 要打包输出的文件名

template: 'index.html', // 打包输出后该html文件的名称

chunks: ['manifest', 'vendor', 'entry'] // 输出的html文件引入的入口chunk

// 还有一些其他配置比如minify、chunksSortMode和本文无关就省略,详见github

}),

new HtmlWebpackPlugin({

filename: 'entry2.html',

template: 'index.html',

chunks: ['manifest', 'vendor', 'entry2']

})

]

}

上面一个配置要注意的是 chunks,如果没有配置,那么生成的 HTML 会引入所有入口 JS 文件,在上面的例子就是,生成的两个 HTML 文件都会引入 entry.jsentry2.js,所以要使用 chunks 配置来指定生成的 HTML 文件应该引入哪个 JS 文件。配置了 chunks 之后,才能达到不同的 HTML 只引入对应 chunks 的 JS 文件的目的。

大家可以看到除了我们打包生成的 chunk 文件 entry.jsentry2.js 之外,还有 manifestvendor 这两个,这里稍微解释一下这两个 chunk

  1. vendor 是指提取涉及 node_modules 中的公共模块;

  2. manifest 是对 vendor 模块做的缓存;

打包完的结果如下:

文件结构:

现在打包出来的样式正是我们所需要的,此时我们在 dist 目录下启动 live-server(如果你没安装的话可以先安装 npm i-g live-server),就可以看到效果出来了:

当前代码:Github - multi-entry-vue2

至此就实现了一个简单的多入口项目的配置。

4. 配置改进

4.1 文件结构改动

我们在前文进行了多入口的配置,要想新建一个新的入口,就复制多个文件,再手动改一下对应配置。

但是如果不同的 HTML 文件下不同的 vue-routervuex 都放到 src 目录下,多个入口的内容平铺在一起,项目目录会变得凌乱不清晰,因此在下将多入口相关的文件放到一个单独的文件夹中,以后如果有多入口的内容,就到这个文件夹中处理。

下面我们进行文件结构的改造:

  1. 首先我们在根目录创建一个 entries 文件夹,把不同入口的 router、 store、 main.js 都放这里,每个入口相关单独放在一个文件夹;

  2. 在 src 目录下建立一个 common 文件夹,用来存放多入口共用的组件等;

现在的目录结构:

.

├── build # 没有改动

├── config # 没有改动

├── entries # 存放不同入口的文件

│   ├── entry1

│   │   ├── router # entry1 的 router

│   │   │   └── index.js

│   │   ├── store # entry1 的 store

│   │   │   └── index.js

│   │   ├── App.vue # entry1 的根组件

│   │   ├── index.html # entry1 的页面模版

│   │   └── main.js # entry1 的入口

│   └── entry2

│   ├── router

│   │   └── index.js

│   ├── store

│   │   └── index.js

│   ├── App.vue

│   ├── index.html

│   └── main.js

├── src

│   ├── assets

│   │   └── logo.png

│   ├── common # 多入口通用组件

│   │   └── CommonTemplate.vue

│   └── components

│   ├── HelloWorld.vue

│   ├── test1.vue

│   └── test2.vue

├── static

├── README.md

├── index.html

├── package-lock.json

└── package.json

4.2 webpack 配置

然后我们在 build/utils 文件中加两个函数,分别用来生成 webpack 的 entry 配置和 HtmlWebpackPlugin 插件配置,由于要使用 node.js 来读取文件夹结构,因此需要引入 fsglob 等模块:

// build/utils

const fs = require('fs')

const glob = require('glob')

const merge = require('webpack-merge')

const HtmlWebpackPlugin = require('html-webpack-plugin')

const ENTRY_PATH = path.resolve(__dirname, '../entries')

// 多入口配置,这个函数从 entries 文件夹中读取入口文件,装配成webpack.entry配置

exports.entries = function() {

const entryFiles = glob.sync(ENTRY_PATH + '/*/*.js')

const map = {}

entryFiles.forEach(filePath => {

const filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)

map[filename] = filePath

})

return map

}

// 多页面输出模版配置 HtmlWebpackPlugin,根据环境装配html模版配置

exports.htmlPlugin = function() {

let entryHtml = glob.sync(ENTRY_PATH + '/*/*.html')

let arr = []

entryHtml.forEach(filePath => {

let filename = filePath.replace(/.*\/(\w+)\/\w+(\.html|\.js)$/, (rs, $1) => $1)

let conf = {

template: filePath,

filename: filename + '.html',

chunks: [filename],

inject: true

}

// production 生产模式下配置

if (process.env.NODE_ENV === 'production') {

conf = merge(conf, {

chunks: ['manifest', 'vendor'],

minify: {

removeComments: true,

collapseWhitespace: true,

removeAttributeQuotes: true

},

chunksSortMode: 'dependency'

})

}

arr.push(new HtmlWebpackPlugin(conf))

})

return arr

}

稍微解释一下这两个函数:

  1. exports.entries 函数从 entries 文件夹中找到二级目录下的 JS 文件作为入口文件,并且将二级目录的文件夹名作为 key,生成这样一个对象: {"entry1":"/multi-entry-vue/entries/entry1/main.js"},多个入口情况下会有更多键值对;

  2. exports.htmlPlugin 函数和之前函数的原理类似,不过组装的是 HtmlWebpackPlugin 插件的配置,生成这样一个数组,可以看到和我们手动设置的配置基本一样,只不过现在是根据文件夹结构来生成的:

// production 下

[

{

template: "/multi-entry-vue/entries/entry1/index.html",

chunks: ['manifest', 'vendor', 'entry1'],

filename: "entry1.html",

chunksSortMode: 'dependency'

},

{ ... } // 下一个入口的配置

]

有了这两个根据 entries 文件夹的结构来自动生成 webpack 配置的函数,下面来改一下 webpack 相关的几个配置文件:

// build/webpack.base.conf.js

module.exports = {

entry: utils.entries(), // 使用函数生成 entry 配置

output: {

path: config.build.assetsRoot,

filename: '[name].js',

publicPath: process.env.NODE_ENV === 'production'

? config.build.assetsPublicPath

: config.dev.assetsPublicPath

}

}

// build/webpack.dev.conf.js

// const HtmlWebpackPlugin = require('html-webpack-plugin') // 不需要了

const devWebpackConfig = merge(baseWebpackConfig, {

devServer: {

historyApiFallback: {

rewrites: [ // 别忘了把 devserver 的默认路由改一下

{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'entry1.html') },

],

}

},

plugins: [

// https://github.com/ampedandwired/html-webpack-plugin

// new HtmlWebpackPlugin({

// filename: 'index.html',

// template: 'index.html',

// inject: true

// }), // 注释掉原来的 HtmlWebpackPlugin 配置,使用生成的配置

].concat(utils.htmlPlugin())

})

// build/webpack.prod.conf.js

// const HtmlWebpackPlugin = require('html-webpack-plugin')

const webpackConfig = merge(baseWebpackConfig, {

plugins: [

// new HtmlWebpackPlugin({

// ... 注释掉,不需要了

// }),

].concat(utils.htmlPlugin())

})

现在我们再 npm run build,看看生成的目录是什么样的:

此时我们在 dist 目录下启动 live-server 看看是什么效果:

当前代码:Github - multi-entry-vue3


网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎指出~

最后

  • 欢迎加我微信(CALASFxiaotan),拉你进技术群,长期交流学习...

  • 欢迎关注「前端巅峰」,认真学前端,做个有专业的技术人...

原创不易,点个在看支持我吧,转发就更好了

结合webpack配置_呕心沥血编写的webpack多入口零基础配置 【建议收藏】相关推荐

  1. webpack打包后引用cdn的js_呕心沥血编写的webpack多入口零基础配置 【建议收藏】...

    最近在做项目的时候遇到了一个场景:一个项目有多个入口,不同的入口,路由.组件.资源等有重叠部分,也有各自不同的部分.由于不同入口下的路由页面有一些是重复的,因此我考虑使用 Webpack 多入口配置来 ...

  2. 消控中心人员配置_物业公司(项目)各岗位员工配置人数及标准(供参考)

    一物业定编的目的1.优化编制结构,加强编制规模的合理化, 满足项目物业系统的人员需求:2.通过编制管理逐步,提升各大区住宅物业服务中心人均管理面积:3.通过编制管理达到物业系统整体扭亏的目标.二物业定 ...

  3. spring.jpa配置_使用JPA和Spring 3.1进行事务配置

    spring.jpa配置 1.概述 本教程将讨论配置Spring Transactions ,使用@Transactional批注和常见陷阱的正确方法 . 有关核心持久性配置的更深入讨论,请查看Spr ...

  4. unity需要什么配置_《英雄联盟》LoL需要什么电脑配置?英雄联盟电脑配置要求。...

    英雄联盟对电脑配置要求极低,我们先看下英雄联盟官方公布的配置. 我们可以看到两个配置推荐,上面的最低配置,下面的是推荐配置,最低配置往往参考价值不大,我们直接看推荐配置. 处理器方面:推荐是的处理器是 ...

  5. ❤️周末爆肝两天❤️,万字长文,手把手教你配置CSDN主页的独特域名(保姆级教程,建议收藏)

    ❤️ 感受下效果图 ❤️ 目录 一.前言 二.先解决有没有的问题 1. 前置条件 2. 购买云服务器 3. 购买DNS域名 4. 配置Apache2服务 5. 配置云服务器的端口映射 6. 配置ngi ...

  6. SSM配置地狱?一篇整合模板迅速解决!【建议收藏】

    Spring+SpringMVC+Mybatis 环境配置 IDEA MySQL 5.7 Tomcat 8.5 Maven 3.6 创建数据库 CREATE DATABASE `ssmbuild`;U ...

  7. 零基础学python 视频_全网最全Python视频教程真正零基础学习Python视频教程 490集...

    Python Web开发-进阶提升 490集超强Python视频教程 真正零基础学习Python视频教程 [课程简介] 这是一门Python Web开发进阶课程,手把手教你用Python开发完整的商业 ...

  8. php编写一个学生类_Python零基础入门之编写测试实例

    测试函数 首先是给出用于测试的代码,如下所示,这是一个接收姓和名然后返回整洁的姓名的函数: def get_formatted_name(first, last):full_name = first ...

  9. java基础不好框架能学会吗_转行Java能学会吗?零基础学习Java的学习路线

    Java的跨平台性.通用性.安全性.高效性决定了这门语言在未来10年都会是最热门的语言之一.Java技术的安全性和平台移植性足够让他应用到不同的领域,它的工作需求足够大,现实一点来说即使Java濒临o ...

最新文章

  1. 综述|深度学习在SLAM定位与建图中的应用(近250篇参考文献)
  2. 独家 | 年度盘点!必看AI顶会论文、Github高星项目大合集(附链接)
  3. centos中用MySQL创建新表_CentOS下使用Shell批量创建数据库表
  4. HashMap为什么是线程不安全的?
  5. 戴尔服务器装新硬盘模式,DELL服务器配置RAID及安装ESXI
  6. jQuery和MooTools的真正区别
  7. mysql workbench入门_5分钟入门MySQL Workbench
  8. python关键词提取源码_Python 结巴分词 关键词抽取分析
  9. QQ邮箱怎么发送文件夹 怎样在QQ邮箱里发送压缩文件夹
  10. html 12306页面实现,jQuery模拟12306城市选择框功能简单实现方法示例
  11. 概率统计23——假设检验理论(2)
  12. pyqt5 登录跳转
  13. 给ftp服务器创建文件夹,ftp服务器创建文件夹
  14. Python手游辅助教程之战双帕弥什脚本教程
  15. 训练指南 UVALive - 5713(最小生成树 + 次小生成树)
  16. python编写自定义函数计算约登值(约登指数、Youden Index)、寻找最佳阈值(threshold、cutoff)、以及最佳阈值对应的ROC曲线中的坐标点
  17. asp毕业设计——基于asp+access的会员管理系统设计与实现(毕业论文+程序源码)——会员管理系统
  18. python小组项目总结报告_给大家汇报一下我们学习小组的进展情况
  19. 简述——什么是软件质量的六大特性?
  20. 世界上最遥远的距离就是?

热门文章

  1. Linux---阻塞与非阻塞、同步与异步的区别
  2. 零点是结束,也是开始。
  3. JavaScript学习(二十三)—scrollTop练习
  4. .NET 是什么意思
  5. 支付宝有50万存款,但欠30万房贷。是还房贷好,还是买基金好?
  6. 从用户空间直接访问系统调用
  7. 为什么那么多公司不用 .NET,而选择PHP、JSP,是 .NET有什么缺点吗?
  8. go 模板(template)的常用基本语法
  9. html+css+javascript实现小游戏2048(详解,附源代码)
  10. deepin linux下解决Qt搜狗输入法无法输入中文