Webpack构建的基于zepto的多页应用脚手架,本文聊聊本次项目中Webpack构建多页应用的一些心得体会。

1.前言

由于公司旧版的脚手架是基于Gulp构建的zepto多页应用(有兴趣可以看看web-mobile-cli),有着不少的痛点。例如:

  1. 需要兼容低版本浏览器,只能采用promise,不能使用awaitgenerator等。(因为babel-runtime需要模块化);
  2. 浏览器缓存不友好(只能全缓存而不是使用资源文件的后缀哈希值来达到局部缓存的效果);
  3. 项目的结构不友好(可以更好的结构化);
  4. 开发环境下的构建速度(内存);
  5. Gulp插件相对Webpack少且久远,维护成本高等等。

这次升级有几个地方需要注意和改进:

  1. 项目旧代码尽量做到无缝转移;
  2. 资源文件的缓存;
  3. 组件式的组织目录结构。

Github仓库:

  1. Gulp构建的旧版多页应用web-mobile-cli;
  2. Webpack构建的多页应用web-mobile-webpack-cli。

2.多页

Webpack的多页应用通过多入口entry和多实例html-webpack-plugin配合来构建,html-webpack-pluginchunk属性传入对应entrykey就可以做到关联,例如:

module.exports = {entry: {pageOne: './src/pageOne/index.js',pageTwo: './src/pageTwo/index.js',pageThree: './src/pageThree/index.js'},plugins: [new HtmlWebpackPlugin({filename: `pageOne.html`,template: `./src/pageOne.html`,chunks: ['pageOne']}),new HtmlWebpackPlugin({filename: `pageTwo.html`,template: `./src/pageTwo.html`,chunks: ['pageTwo']}),new HtmlWebpackPlugin({filename: `pageTwo.html`,template: `./src/pageTwo.html`,chunks: ['pageTwo']})]
}
复制代码

那么问题来了,开发新的页面每次都得添加岂不是很麻烦。这里推荐神器glob根据正则规则匹配。

const glob = require('glob')module.exports = {entry: glob.sync('./src/js/*.js').reduce((pre, filepath) => {const tempList = filepath.split('src/')[1].split(/js\//)const filename = `${tempList[0]}${tempList[1].replace(/\.js/g, '')}`return Object.assign(pre, {[filename]: filepath})}, {}),plugins: [...glob.sync('./src/html/*.ejs').map((filepath, i) => {const tempList = filepath.split('src/')[1].split(/html\//)const fileName = tempList[1].split('.')[0].split(/[\/|\/\/|\\|\\\\]/g).pop()const fileChunk = `${tempList[0]}${fileName}`return new HtmlWebpackPlugin({filename: `${fileChunk}.html`,template: filepath,chunks: [fileChunk]})})]
}
复制代码

3.模板

项目没有直接使用html,而是使用了ejs作为模板,这里有至少两个好处:

  1. 把公共的代码抽离出来;
  2. 传入公共的变量。
// header.ejs
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge"><title><%= title %></title>
</head>// index.ejs
<!DOCTYPE html>
<html lang="en">
<% include ./header.ejs %>
<body><!-- page -->
</body>
<script src="<%= publicPath %>lib/zepto.js"></script>
</html>
复制代码

<% include ./header.ejs %>就是引用了header.ejs文件,<%= title %><%= publicPath %>是我在配置文件定义的两个变量,publicPath是为了统一cdn缓存服务器的域名,非常有用。

4.垫片

项目中使用了zepto,所以需要垫片,所谓垫片就是shim 预置依赖,即全局依赖。

webpack compiler 能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。然而,一些 third party(第三方库) 可能会引用一些全局依赖(例如 jQuery 中的 $)。因此这些 library 也可能会创建一些需要导出的全局变量。这些 "broken modules(不符合规范的模块)" 就是 shim(预置依赖) 发挥作用的地方。

垫片有两种方式:

  1. 传统方式的垫片就是在html文件中,所有引用的js文件的最前面引用的文件(例如zepto);
  2. Webpack配置shim预置依赖

最终我选择了Webpack配置shim预置依赖这种方式,因为:

  1. 传统的方式需要每个页面都手动引入(虽说搭配ejs可以抽离出来成为公共模块,但还是需要每个页面手动引入公共模块);
  2. 传统的方式需要多发一次请求去请求垫片;
  3. Webpack可以把所有第三方插件的代码都拆分打包成为一个独立的chunk,只需一个请求。
module.exports = {entry: {...},module: {rules: [{test: require.resolve('zepto'),use: 'imports-loader?this=>window'}]},plugins: [new webpack.ProvidePlugin({$: 'zepto'})]
}
复制代码

5.拆分

一般来讲Webpack的配置entry中每个key就对应输出一个chunk,那么该项目中会提取这几类chunk

  1. 页面入口(entry)对应的chunk
  2. common:多次引用的公共文件;
  3. vender:第三方依赖;
  4. manifestWebpack运行时(runtime)代码,它存储着Webpackmodulechunk的信息。
module.exports = {entry: {...},module: {...},plugins: [],optimization: {runtimeChunk: {name: 'manifest'},splitChunks: {cacheGroups: {vendors: {test: /[\\/]node_modules[\\/]/,chunks: 'all',name: 'vendors',filename: 'js/vendors.[contenthash:8].js',priority: 2,reuseExistingChunk: true},common: {test: /\.m?js$/,chunks: 'all',name: 'common',filename: 'js/common.[contenthash:8].js',minSize: 0,minChunks: 2,priority: 1,reuseExistingChunk: true}}}}
}
复制代码

这里注意的有两点:

  1. 优先顺序:第三方插件的prioritycommon代码的priority大;
  2. 提取common代码:minChunks为引用次数,我设置为引用2次即提取为公共代码。minSize为最小字节,设置为0。

6.缓存

缓存的目的是为了提高加载速度,Webpack在缓存方面已经是老生常谈的了,每个文件赋予唯一的hash值,只有更新过的文件,hash值才改变,以达到整体项目最少文件改动。

6.1 hash值

Webpack中有三种hash值:

  1. hash:全部文件同一hash,一旦某个文件改变,全部文件的hash都将改变(同一hash不满足需求);
  2. chunkhash:根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值(问题是css作为模块importJavaScript文件中的,它们的chunkhash是一致的,一旦改变js文件,即使importcss文件内容没有改变,其chunkhash值也会一同改变,不满足需求);
  3. contexthash:只有模块的内容变了,那么hash值才改变(采用)。
module.exports = {entry: {pageOne: './src/pageOne/index.js',pageTwo: './src/pageTwo/index.js',pageThree: './src/pageThree/index.js'},output: {path: 'src',chunkFilename: 'j[name].[contenthash:8].js',filename: '[name].[contenthash:8].js'},plugins: [new HtmlWebpackPlugin({filename: `pageOne.html`,template: `./src/pageOne.html`,chunks: ['pageOne']}),new HtmlWebpackPlugin({filename: `pageTwo.html`,template: `./src/pageTwo.html`,chunks: ['pageTwo']}),new HtmlWebpackPlugin({filename: `pageTwo.html`,template: `./src/pageTwo.html`,chunks: ['pageTwo']})]
}
复制代码

6.2 module id

仅仅使用contexthash还不足够,每当import的资源文件顺序改变时,chunk依然会改变,目的没有达成。要解决这个问题首先要理解modulechunk分别是什么,简单理解:

  1. module:一个import对应一个module(例如:import zepto from 'zepto'中的zepto就是一个module);
  2. chunk:根据配置文件打包出来的包,就是chunk。(例如多页应用中每个entrykey值对应的文件)。

因为Webpack内部维护了一个自增的id,依照顺序赋予给每个module,每当新增或者删减导致module的顺序改变时,受影响的chunkhash值也会改变。解决办法就是使用唯一的hash值替代自增的id

module.exports = {entry: {...},module: {...},plugins: [],optimization: {moduleIds: 'hashed'}
}
复制代码

7.优化

优化的目的是提高执行和打包的速度。

7.1 查找路径

告诉Webpack解析模块时应该搜索的目录,缩小编译范围,减少不必要的编译工作。

const {resolve} = require('path')module.exports = {entry: {...},module: {...},plugins: [],optimization: {...},resolve: {alias: {'@': resolve(__dirname, '../src'),},modules: [resolve('src'),resolve('node_modules'),]}
}
复制代码

7.2 指定目录

指定loaderinclude目录,作用是缩小编译范围。

const {resolve} = require('path')module.exports = {entry: {...},module: {rules: [{test: /\.css$/,include: [resolve("src"),],use: ['style-loader', 'css-loader']}]},plugins: [],optimization: {...},resolve: {...}
}
复制代码

7.3 babel缓存目录

babel-loader开始缓存目录cacheDirectory

const {resolve} = require('path')module.exports = {entry: {...},module: {rules: [{test: /\.m?js$/,exclude: /(node_modules|bower_components)/,include: [resolve("src"),],use: {loader: 'babel-loader',options: {cacheDirectory: true,presets: ['@babel/preset-env'],plugins: ['@babel/plugin-transform-runtime']}}}]},plugins: [],optimization: {...},resolve: {...}
}
复制代码

7.4 插件TerserJSPlugin

TerserJSPlugin插件的作用是压缩JavaScript,优化的地方是开启缓存目录和开启多线程。

const {resolve} = require('path')module.exports = {entry: {...},module: {...},plugins: [],optimization: {minimizer: [new TerserJSPlugin({parallel: true,cache: true,})]},resolve: {...}
}
复制代码

8.总结

通过这次学习Webpack到升级脚手架,对前端工程化有了进一步的了解,也感受到了Webpack4带来的开箱即用,挺方便的。

参考文章:
Webpack官方文档
【实战】webpack4 + ejs + express 带你撸一个多页应用项目架构
基于 webpack 的持久化缓存方案

Webpack构建多页应用心得体会相关推荐

  1. 使用webpack构建多页应用

    背景 随着react, vue, angular 三大前端框架在前端领域地位的稳固,SPA应用正在被应用到越来越多的项目之中.然而在某些特殊的应用场景之中,则需要使用到传统的多页应用.在使用webpa ...

  2. Vue全家桶 + webpack 构建单页应用初体验

    文章指南 主题   承接这上一篇Vue + Webpack 构建模块化开发框架详解,我们知道了如何使用webpack对vue进行打包,从而开始我们的前端模块化开发之路,这一篇在上一篇的基础上讲解 Vu ...

  3. 《构建之法》心得体会

    第一章概述告诉了我们软件的特性,什么是软件,也就是软件=程序+软件工程,软件是可以运行在计算机以及电子设备中的指令和数据的有序集合.而软件工程是把系统的.有序的.可量化的方法应用到软件的开发.运营和维 ...

  4. 读《构建之法》的心得体会

    前段时间,我看了<构建之法>的一些内容,有了一些心得体会. 软件工程所讨论的是代码量巨大.涉及人数众多.项目需求多变时所要解决的问题.而在校学生根本就没有这样的环境.而邹欣老师的<构 ...

  5. 计算机ppt制作实验报告总结,ppt实验报告心得体会[工作范文](18页)-原创力文档...

    ppt实验报告心得体会 篇一:PPT实训报告总结 丽水学院计算机与信息工程学院实验 报告实 验报告 实验4用ppt制作演示型课件(设计性实验) 一.实验目的 熟悉office 软件的编辑制作环境:熟练 ...

  6. 计算机实训报告心得怎么写,计算机实训报告心得体会范文_计算机实训总结怎么写...

    计算机实训室建设所涉及的规范及技术繁杂,为提高建设质量,必须在计算机实训室建设前期做好计算机实训室工程设计.下面是学习啦带来的计算机实训报告心得体会范文,欢迎大家阅读. 计算机实训报告心得体会范文篇一 ...

  7. 小学计算机室培训心得,小学计算机培训心得体会范文

    <小学计算机培训心得体会范文>由会员分享,可在线阅读,更多相关<小学计算机培训心得体会范文(3页珍藏版)>请在人人文库网上搜索. 1.小学计算机培训心得体会范文 20xx年10 ...

  8. 高中计算机听课总结,中学新信息技术老师听课心得体会五篇

    <中学新信息技术老师听课心得体会五篇>由会员分享,可在线阅读,更多相关<中学新信息技术老师听课心得体会五篇(14页珍藏版)>请在人人文库网上搜索. 1.中学新信息技术老师听课心 ...

  9. 计算机课程作品观摩,计算机观摩教学活动心得体会(共7篇)

    教学观摩活动心得体会 各位领导.各位老师: 大家下午好! 前不久有幸跟随肖校长到宜兴外国语学校参加了苏.浙.鲁初中名校课堂文化论坛暨教学观摩活动.短短一天半的时间,我们聆听了江苏宜兴外国语学校.山东杜 ...

最新文章

  1. Qt探索之路——获取QTextEdit文本内容
  2. 【机器学习基础】撒花!李宏毅机器学习 2021 版正式开放上线
  3. Dubbo 2.7三大特性详解
  4. python list append tuple_Python之list、tuple、dict、set
  5. Uber 和通用拟开源自动驾驶可视化软件
  6. 敏捷开发般若敏捷系列之三:什么是敏捷(下)(无住,不住于空,破空执,非法,非非法)...
  7. ALERT日志中常见监听错误:ORA-3136错误的排查
  8. Quick-cocos2d-x luabinding
  9. 「管理数学基础」3.3 凸分析:凸函数的极值和凸规划
  10. HTML5 响应式网页设计之页面美化(一.响应式布局)
  11. python3.4 or 3.x xlwt replaced with xlwt-future
  12. [转]Using TRY...CATCH in Transact-SQL
  13. 用Mediator Pattern + Queue 解决 订单处理流程
  14. linux top 网络,Linux Top 详解
  15. bochs运行xp_bochs安卓最新版下载
  16. Js判断数组中是否有某值
  17. 入门图形学:ComputeShader
  18. 愚人节将至,怎么恶搞最过瘾
  19. MMS-MTK-Obigo03c
  20. 构造启发式算法:最邻近插入法

热门文章

  1. P3197 [HNOI2008]越狱(快速幂)
  2. crawler碎碎念5 豆瓣爬取操作之登录练习
  3. 包转发、吞吐量、背板带宽计算
  4. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法...
  5. GitLab搭建详细过程
  6. php.ini设置相关信息汇总
  7. 转载--va_list
  8. 从实验现象详细分析BGP的路由策略与选路原则
  9. 再赠邓超明(帮别人名字作诗)
  10. Caffe2:python -m caffe2.python.operator_test.relu_op_test