Vue+ElementUI项目使用webpack输出MPA的方法


Posted in Javascript onAugust 27, 2019

一. 需求分析

为另一个项目提供可嵌入的功能单页,大部分页面使用时都是独立功能页,个别页面带有左侧边栏(相当于3-4个页面的整合形态),由于资源定位地址的限定,每个页面打包为单页后,入口html文件需要定制命名,且脚本和样式文件需要放在指定的路径下,公共资源地址也必须替换成特殊字符以适配母系统的调用逻辑(比如下面结构中应用jquery.min.js的路径可能是{{publicRoot}}/{{publicLib}}/jquery.minjs)。假设原工程中拥有AB这2个旧页面,现在需要开发CDE这3个页面,目录结构要求如下:

Vue+ElementUI项目使用webpack输出MPA的方法

蓝色部分为旧资源,绿色部分为新开发需求。

二. 原方案分析

原方案采用Vue+ElementUI进行开发,构建过程基本是零配置的,开发效率非常高,页面风格也统一,但零配置的构建过程只能生成SPA模式的应用,所以原方案的做法是:

将构建过程中需要定制的量提取到config.js文件中进行统一管理,大致形式如下:

//config.js
module.exports = {
  A:{
    publicPath:'{{publicRoot}}/{{publicLib}}'
    prodFileName:'A.html',
    entryKey:'public/A',
    entryPath:'public/A/A.js'
  },
  B:{
    //...
  }
  //...
}

开发过程中使用统一的路由文件router.js,打包过程中在main.js中引用对应页面的XX.router.spa.js作为路由,而将其他页面注释掉,打包时传入命令行参数--key=XXX,key值在打包脚本中被解析后从config.js中取出打包需要的设置参数,然后将目标页面打包为独立页面,其他页面虽然也在工程中,但并不参与打包。

// 入口文件src/main.js
import router from './pages/C/router.spa';
//import router from './pages/D/router.spa';
//import router from './pages/E/router.spa';

上述打包过程在使用中出现了很多问题:

  • 公共依赖没有剥离,vueElementUI会被打包进每一个单页面,使得每个打包出的index.js几乎有1.2MB大小,这种空间浪费是没必要的。
  • 公共样式没有形成独立文件,这使得每当有样式细节发生变更,就需要手动将每个页面逐一进行重新出包。
  • 页面增多后在main.js中会有很多独立路由,如果开发中进行了跨页面修改,很可能在main.js中激活的路由为C页面路由时,打包时--key参数的值却传成了D,这种情况并不会引起报错,但事实上构建结果确实错误的。
  • 由于入口文件保持main.js没有变化,所以在不同页面打包时,结果都输出在dist目录下,需要手动与母工程中的地址去匹配,操作繁琐。

三. 多页面改造3步走

上面的问题实际上都是因为原方案将一个多页面开发需求按照单页面应用来实现而造成的,需要对自动化构建工程进行一些定制。

1.分离webpack配置

本例中开发环境和最终打包的主要差异在于路由上,开发中由于可能需要进行跨页面开发,可以使用单入口和独立路由,而进行生产环境构建时则需要输出多页面应用,所以首先要做的就是将原本的webpack.config.js文件拆分为webpack.base.js,webpack.dev.js,webpack.prod.js三个文件,webpack.base.js为环境无差别的配置,然后依据构建模式的不同,使用webpack-merge插件将环境相关的配置与基本配置进行合并:

/*webpack.base.js示例*/
const argv = require('yargs-parser')(process.argv.slice(2));
const env_short = argv.env.all ? 'all' : argv.p ? 'prod':'dev';
const webpackConfig = require(`./config/webpack.${env_short}`);//根据-p属性加载webpack的dev配置或prod配置
const merge = require('webpack-merge');

//基本配置
const baseConfig = {
  //....
}

//输出合并后的配置
module.exports = merge(baseConfig, webpackConfig);

webpack.dev.js保持原本的SPA开发的设置即可满足需求。

2. 抽离外部引用

本例中较大的外部应用是vue和ElementUI,很多开发者一直使用自动化脚手架工具,并没有意识到这两个库作为外部依赖该如何引入工程。公共库的抽离需要在webpack配置中将其填写在external配置项中:

module.exports = {
 //...
 externals:{
   vue:'Vue',
   'element-ui':'ELEMENT'
 },
 //...
}

key为引用的模块名,value为这个模块引入后对应的全局命名,external配置项的含义是:请不要将这个模块注入编译后的JS文件里,对于源代码里出现的任何import/require这个模块的语句,请将它保留并根据模块化标准进行依赖方式适配 。

Tips:

Vue做为外部依赖时有很多构建包,本例中因为使用webpack进行了构建,没有在线编译模板的需求,所以不需要引入完整的Vue,而只需要引入压缩后的只包含运行时的版本vue.runtime.min.js即可。外部引入库时需要注意命名,比如上例中的ELEMENT,开发者通常会填写为自己在代码中使用的ElementUI而引起报错,当不确定名称时,有个简单的办法就是找一个CDN的资源看一下,通常代码最开始都是UMD规范的固定结构,很容易看到关键词(如下图所示)。

Vue+ElementUI项目使用webpack输出MPA的方法

然后将资源的CDN地址或是本地公共库地址加入到index.html中,你可以使用模板语法,然后从html-webpack-plugin插件实例化时传入定制参数:

<!--html文件模板-->
<body>
 <div id="app"></div>
 <script src="<%= htmlWebpackPlugin.options.vue_path %>"></script>
 <script src="<%= htmlWebpackPlugin.options.elementUI_path %>"></script>
 <script src="<%= htmlWebpackPlugin.options.tpl_entryPath %>/index.js"></script>
</body>
//webpack.prod.js
module.exports = {
  //...
 plugins: [
  new HtmlWebpackPlugin({
   template: 'src/index.html',//生成index.html时依据的模板
   filename: '.....',
   inject:false,
   tpl_entryPath:'....',
   vue_path:'.....',
   elementUI_path:'.....',
  }),
  //new BundleAnalyzerPlugin()
 ],
}

最终打包后生成的index.html文件大致如下:

<body>
 <div id="app"></div>
 <script src="{{publicRoot}}/{{publicLib}}/vue.min.js"></script>
 <script src="{{publicRoot}}/{{publicLib}}/element-ui.js"></script>
 <script src="public/A/A.js"></script>
</body>

如果第三方库从本地加载,则需要将/node_modules/element-ui/lib/index.js和/node_modules/vue/dist/vue.runtime.min.js两个依赖文件拷贝到lib文件夹中的对应地址,这样访问index.html时就可以以外部依赖的形式将其加载进来。样式文件的剥离直接使用插件完成即可,webpack4以前的版本使用extract-text-webpack-plugin,从4.0版本后统一使用mini-css-extract-plugin。

3. 为webpack定制多入口

多入口的配置是多页面应用打包的关键,由于打包结果存在嵌套目录,所以需要对entry对象的键值进行一些定制,打包后的路径信息是直接通过key值来定制的,同时需要实例化多个HtmlWebpackPlugin来为每一个入口文件生成一个对应的index.html访问入口,定制参数可以在实例化时传入:

//webpack.prod.js
module.exports = {
  entry:{
    'C/index':'./src/pages/C/C.entry.js',
    'DESK/D/index':'./src/pages/D/D.entry.js',
    'DESK/E/index':'./src/pages/E/E.entry.js'
  }
  //...
  plugins:[
    new HtmlWebpackPlugin({...paramsC}),
    new HtmlWebpackPlugin({...paramsD}),
    new HtmlWebpackPlugin({...paramsE}),
  ]
}

当然你可以将entry或plugins数组的组装过程剥离到其他文件中,然后直接引用:

Vue+ElementUI项目使用webpack输出MPA的方法

当然,每个页面的入口文件X.entry.js相当于旧方案中main.js文件中移除被注释掉的未启用路由信息后剩余的部分,它足以支撑每个单页独立被访问。

四. 小结

经上述改造后,在dist目录中输出的结构和需求中public目录下的结构就保持一致了,而且每个页面的index.js文件也缩小到了100K左右。当然你也可以使用node.js去编写一些自动化脚本,将后续的替换过程也自动化,或者继续对webpack的打包过程进行优化,本文就不再赘述了。

以上所述是小编给大家介绍的Vue+ElementUI项目使用webpack输出MPA的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
javascript iframe内的函数调用实现方法
Jul 19 Javascript
Jquery 滑入滑出效果实现代码
Mar 27 Javascript
jquery通过closest选择器修改上级元素的方法
Mar 17 Javascript
跟我学习javascript的循环
Nov 18 Javascript
jQuery+css3实现转动的正方形效果(附demo源码下载)
Jan 27 Javascript
js和jq使用submit方法无法提交表单的快速解决方法
May 17 Javascript
vue 页面加载进度条组件实例
Feb 05 Javascript
微信小程序 MinUI组件库系列之badge徽章组件示例
Aug 20 Javascript
前后端如何实现登录token拦截校验详解
Sep 03 Javascript
js获取浏览器地址(获取第1个斜杠后的内容)
Sep 03 Javascript
nuxt框架中对vuex进行模块化设置的实现方法
Sep 06 Javascript
JS实现刷新网页后之前浏览位置保持不变示例详解
Aug 14 Javascript
解决jquery validate 验证不通过后验证正确的信息仍残留在label上的方法
Aug 27 #jQuery
Net微信网页开发 使用微信JS-SDK获取当前地理位置过程详解
Aug 26 #Javascript
javascript实现抢购倒计时程序
Aug 26 #Javascript
VUE路由动态加载实例代码讲解
Aug 26 #Javascript
vue项目部署到nginx/tomcat服务器的实现
Aug 26 #Javascript
vue-router路由模式详解(小结)
Aug 26 #Javascript
vue+moment实现倒计时效果
Aug 26 #Javascript
You might like
php中的ini配置原理详解
2014/10/14 PHP
CodeIgniter开发实现支付宝接口调用的方法示例
2016/11/14 PHP
PHP+JavaScript实现无刷新上传图片
2017/02/21 PHP
基于JavaScript 下namespace 功能的简单分析
2013/07/05 Javascript
使用js在页面中绘制表格核心代码
2013/09/16 Javascript
一个不错的仿携程自定义数据下拉选择select
2014/09/01 Javascript
基于javascript实现判断移动终端浏览器版本信息
2014/12/09 Javascript
javascript实现了照片拖拽点击置顶的照片墙代码
2015/04/03 Javascript
jQuery选择器总结之常用元素查找方法
2016/08/04 Javascript
JavaScript实现网页头部进度条刷新
2017/04/16 Javascript
node.js学习之断言assert的使用示例
2017/09/28 Javascript
vue.js项目nginx部署教程
2018/04/05 Javascript
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
2019/01/19 Javascript
kafka调试中遇到Connection to node -1 could not be established. Broker may not be available.
2019/09/17 Javascript
vue的路由映射问题及解决方案
2019/10/14 Javascript
关于JavaScript中异步/等待的用法与理解
2020/11/18 Javascript
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
2021/01/05 Vue.js
python求众数问题实例
2014/09/26 Python
Python中的元类编程入门指引
2015/04/15 Python
Python实现计算文件夹下.h和.cpp文件的总行数
2015/04/23 Python
Python实现树的先序、中序、后序排序算法示例
2017/06/23 Python
python实现二分查找算法
2017/09/21 Python
详解Python logging调用Logger.info方法的处理过程
2019/02/12 Python
python求最大公约数和最小公倍数的简单方法
2020/02/13 Python
Python爬取新型冠状病毒“谣言”新闻进行数据分析
2020/02/16 Python
使用 Python ssh 远程登陆服务器的最佳方案
2020/03/06 Python
html2canvas截图空白问题的解决
2020/03/24 HTML / CSS
Priority Pass机场贵宾室会籍计划:全球超过1200间机场贵宾室
2018/08/26 全球购物
出纳员的岗位职责
2014/02/22 职场文书
中国梦我的梦演讲稿
2014/04/23 职场文书
入党积极分子自我批评思想汇报
2014/10/10 职场文书
个人整改措施书面材料
2014/10/24 职场文书
面试复试通知单
2015/04/24 职场文书
新学期开学寄语2016
2015/12/04 职场文书
Python如何把不同类型数据的json序列化
2021/04/30 Python
为什么在foreach循环中JAVA集合不能添加或删除元素
2021/06/11 Java/Android