浅谈webpack+react多页面开发终极架构


Posted in Javascript onNovember 11, 2018

webpack在单页面打包上应用广泛,以create-react-app为首的脚手架众多,单页面打包通常指的是将业务js,css打包到同一个html文件中,整个项目只有一个html文件入口,但也有许多业务需要多个页面不同的入口,比如不同的h5活动,或者需要支持seo的官方网站,都需要多个不同的html。webpack-react-multi-page架构让你可以在多页面在项目开发中自动化打包新创建页面并保证每个页面都可以热更新 ,build打包后有清晰的文件层次结构。

概览

key value
名称 webpack+react多页面架构
描述 简单易用的多页面自动化开发架构
开发者 leinov
发布日期 2018-11-07
版本 2.0
仓库 github地址

特性

  • 支持多页面同时热加载开发
  • 自动识别新创建页面
  • 每个页面生成个性化信息
  • 分类打包
  • 灵活扩展

安装&使用

// clone
git clone git@github.com:leinov/webpack-react-multi-page.git

// 安装依赖包
npm install

// 开发
npm run dev

// 编译打包
npm run build

// 启动生产页面
npm start

新创建页面在src下添加文件夹并创建pageinfo.json 然后npm run dev 即可

|-- src
  |-- index/
  |-- page2/
    |-- index.js
    |-- pageinfo.json

项目架构

技术使用

  • react16
  • webpack4
    • html-webpack-plugin 生成html文件
    • mini-css-extract-plugin css分离打包
    • uglifyjs-webpack-plugin js压缩
    • optimize-css-assets-webpack-plugin css压缩
  • es6
  • babel
  • node
    • opn 打开浏览器
    • compression 开启gzip压缩
    • express
    • fs
  • git

目录结构

|-- webpack-react-multi-pages //项目
  |-- dist //编译生产目录
    |-- index
      |-- index.css
      |-- index.js
    |-- about
      |-- about.css
      |-- about.js
    |-- images
    |-- index.html
    |-- about.html
  |-- node_modules //node包
  |-- src //开发目录
    |-- index //index页面打包入口
      |-- images/
      |-- js
        |-- app.js// 业务js
      |-- index.sass
      |-- index.js //页面js入口
    |-- about //about页面打包入口
      |-- images/
        |--js
          |-- app.js// 业务js
      |-- about.sass
      |-- about.js //页面js入口
    |-- template.html // html模板
    |-- style.sass //公共sass
  |-- webpackConfig //在webpack中使用
    |-- getEntry.js //获取入口
    |-- getFilepath.js //src下需要打包页面文件夹
    |-- htmlconfig.js //每个页面html注入数据
  |-- package.json
  |-- .gitignore
  |-- webpack.config.js //webpack配置文件
  |-- www.js //生产启动程序

wiki

webpack打包单页面应用

webpack在单页面打包上应用广泛,以create-react-app为首的接触脚手架众多,单页面打包通常指的是将业务js,css打包到同一个html文件中,整个项目只有一个html文件入口

webpack单页面打包配置

webpack.config.js

module.exports = (env, argv) => ({
  entry: ".src/index.js",
  output: {
    path: path.join(__dirname, "dist"),
    filename: "bundle.js"
  },
  module: {
    rules: [
    ...
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "首页",
      filename:"index.html",
      favicon:"",
      template: "./src/template.html",

    })
  ]
});

这样就可以在dist文件夹下打包出一个下面这样的文件

<!DOCTYPE html>
<html lang="en">
  <head>
  <title>首页</title>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="bundle.js"></script></body>
</html>

webpack多页面打包配置

webpack 的entry支持两种种格式

打包单个文件

module.exports = {
 entry: '.src/file.js',
 output: {
  path: path.resolve(__dirname, 'dist'),
  filename: 'bundle.js'
 }
};

这样就会在dist下打包出一个bundle.js

打包出多个文件

module.exports = {
 entry: {
  index:"./src/index.js",
  about:"./src/about.js"
 },
 output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].js' index.js,about.js这两个文件
 }
};

上面在dist下打包出两个与entry属性名对应的js文件

将每个js挂载到相应的html文件上

这里我们需要用到html-webpack-plugin这个webpack插件,每添加一个页面就需要在plugins添加一个new HtmlWebpackPlugin({....})

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => ({
  entry: {
    index:"./src/index.js",
    about:"./src/about.js"
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js' index.js,about.js这两个文件
  }
  ....//其他配置
  plugins: [
    new HtmlWebpackPlugin(
      {
        filename:"index.html",//生成的index.html
        template: "./src/template.html",}) //模板
        chunks:["index"]
      }),
    new HtmlWebpackPlugin(
      {
        filename:"about.html",//生成的index.html
        template: "./src/template.html",}) //模板
        chunks:["index"]
      })
  ]
})

html-webpack-plugin 会通过 template.html 模板生成对应的filename名的html文件,并一并打包到output中对应的文件夹下,注意,在没有特殊配置的情况下所有打包的文件都是对应到output中 path 这个目录下,也包括html。这里的 chunks 需要注意,它是确定该html需要引入哪个js,如果没写的话,默认会引出所有打包的js,当然这不是我们想要的。

上面的配置最终可以在dist下打包出下面的文件结构

|-- dist
  |-- index.js
  |-- about.js
  |-- index.html //内挂载index.js
  |-- about.html //内挂载about.js

通过上面这样的配置,再加上devServer,我们已经可以实现多页面的配置开发了,但这样很不智能,因为你每增加一个页面,就要在wepback里面配置一次,会非常繁琐,所以我们来优化下,让我们只专注于开发页面,配置交给webpack自己.

webpack多页面配置优化

我们再看下src下面的文件结构

|-- src
  |-- index
    |-- app.js
    |-- index.scss
    |-- index.js
  |-- about
    |-- app.js
    |-- index.scss
    |-- index.js

src下面每个文件夹对应一个html页面的js业务,如果我们直接把文件夹对应入口js找到并把他们合并生成对应的entry,那是不是就不用手动写entry了呢,是的!

遍历文件目录

/* eslint-env node */

/**
 * @file: getFilePath.js 遍历文件目录
 * @author: leinov
 * @date: 2018-10-11
 */

const fs = require("fs");

/**
 * 【遍历某文件下的文件目录】
 *
 * @param {String} path 路径
 * @returns {Array} ["about","index"]
 */
module.exports = function getFilePath(path){
  let fileArr = [];
  let existpath = fs.existsSync(path); //是否存在目录
  if(existpath){
    let readdirSync = fs.readdirSync(path); //获取目录下所有文件
    readdirSync.map((item)=>{
      let currentPath = path + "/" + item;
      let isDirector = fs.statSync(currentPath).isDirectory(); //判断是不是一个文件夹
      if(isDirector && item !== "component"){ // component目录下为组件 需要排除
        fileArr.push(item);
      }
    });
    return fileArr;
  }
};

比如在src下有index页面项目,about项目 遍历结果为["index","about"];

遍历生成打包入口数组

/* eslint-env node */
/**
 * @file: getEntry.js 获取entry文件入口
 * @author: leinov
 * @date: 2018-10-11
 * @update: 2018-11-04 优化入口方法 调用getFilePath
 */
const getFilePath = require("./getFilepath");
/**
 * 【获取entry文件入口】
 *
 * @param {String} path 引入根路径
 * @returns {Object} 返回的entry { "about/aoubt":"./src/about/about.js",...}
 */
module.exports = function getEnty(path){
  let entry = {};
  getFilePath(path).map((item)=>{
    /**
     * 下面输出格式为{"about/about":"./src/aobout/index.js"}
     * 这样目的是为了将js打包到对应的文件夹下
     */
    entry[`${item}/${item}`] = `${path}/${item}/index.js`;
  });
  return entry;
};

这里我们使用getFilepath获取的数组,在获取到每个目录下的js文件,组合成一个js入口文件的如下格式的对象。

{
  "index/index":"./src/index/index.js",
  "about/about":"./src/about/index.js"
}

在webpack中使用getEntry

const getEntry = require("./webpackConfig/getEntry");
const entry = getEntry();

module.exports = (env, argv) => ({
  entry: entry,
})

这样我们就自动获取到了entry

html-webpack-plugin自动配置

因为每个页面都需要配置一个html,而且每个页面的标题,关键字,描述等信息可能不同,所以我们在每个页面文件夹下创建一个pageinfo.json,通过fs模块获取到json里信息再遍历到对应得html-webpack-plugin中生成一个html插件数组。

index/pageinfo.json 生成index.html页面信息

{
  "title":"首页",
   "keywords":"webpack多页面"
}

about/pageinfo.json 生成about.html页面信息供

{
  "title":"关于页面",
  "keywords":"webpack多页面关于页面"
}

通过fs遍历读取并生成HtmlWebpackPlugin数组供webpack使用

遍历html插件数组

/**
 * @file htmlconfig.js 页面html配置
 * @author:leinov
 * @date: 2018-10-09
 * @update: 2018-11-05
 * @use: 动态配置html页面,获取src下每个文件下的pageinfo.json内容,解析到HtmlWebpackPlugin中
 */

const fs = require("fs");
const HtmlWebpackPlugin = require("html-webpack-plugin");//生成html文件
const getFilePath = require("./getFilepath");
let htmlArr = [];

getFilePath("./src").map((item)=>{
  let infoJson ={},infoData={};
  try{
    // 读取pageinfo.json文件内容,如果在页面目录下没有找到pageinfo.json 捕获异常
    infoJson = fs.readFileSync(`src/${item}/pageinfo.json`,"utf-8");//
    infoData = JSON.parse(infoJson);
  }catch(err){
    infoData = {};
  }
  htmlArr.push(new HtmlWebpackPlugin({
    title:infoData.title ? infoData.title : "webpack,react多页面架构",
    meta:{
      keywords: infoData.keywords ? infoData.keywords : "webpack,react,github",
      description:infoData.description ? infoData.description : "这是一个webpack,react多页面架构"
    },
    chunks:[`${item}/${item}`], //引入的js
    template: "./src/template.html",
    filename : item == "index" ? "index.html" : `${item}/index.html`, //html位置
    minify:{//压缩html
      collapseWhitespace: true,
      preserveLineBreaks: true
    },
  }));
});

module.exports = htmlArr;

wbpack终极配置

const path = require("path");
const getEntry = require("./webpackConfig/getEntry"); //入口配置
const entry = getEntry("./src");
const htmlArr =require("./webpackConfig/htmlConfig");// html配置

module.exports = (env, argv) => ({
  entry: entry
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
  ....//其他配置
  devServer: {
  port: 3100,
  open: true,
  },
  plugins: [
    ...htmlArr
  ]
})

这样一个自动化完整的多页面架构配置就完成了,如果我们要新创建一个页面

1.在src下创建一个文件目录
2.在新创建的文件目录下添加index.js(必须,因为是webpack打包入口文件)
3.在新创建文件夹下添加pageinfo.json(非必须) 供html插件使用
4.npm run dev开发

完整代码参考项目code

版本

版本 日期 分支 备注
2.0 2018-11-08 master 优化html插件自动化
1.0 2018-10-07 version1.0 第一版

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

Javascript 相关文章推荐
中文字符串截取的js函数代码
Apr 17 Javascript
解析JavaScript中点号“.”的多义性
Dec 02 Javascript
解决angular的post请求后SpringMVC后台接收不到参数值问题的方法
Dec 10 Javascript
jQuery Ajax 全局调用封装实例代码详解
Jun 02 Javascript
JS实现队列的先进先出功能示例
May 10 Javascript
搭建基于express框架运行环境的方法步骤
Nov 15 Javascript
微信小游戏之使用three.js 绘制一个旋转的三角形
Jun 10 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
Aug 29 Javascript
element-ui中按需引入的实现
Dec 25 Javascript
js实现翻牌小游戏
Jul 31 Javascript
解决vue使用vant下拉框van-dropdown-item 绑定title值不变问题
Aug 05 Javascript
js中Map和Set的用法及区别实例详解
Feb 15 Javascript
Vue项目引进ElementUI组件的方法
Nov 11 #Javascript
webpack中如何使用雪碧图的示例代码
Nov 11 #Javascript
vue组件从开发到发布的实现步骤
Nov 11 #Javascript
详解如何在Node.js的httpServer中接收前端发送的arraybuffer数据
Nov 11 #Javascript
基于Vue实现图片在指定区域内移动的思路详解
Nov 11 #Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
Nov 11 #Javascript
详解Vue 动态组件与全局事件绑定总结
Nov 11 #Javascript
You might like
PHP的面试题集
2006/11/19 PHP
如何使用纯PHP实现定时器任务(Timer)
2015/07/31 PHP
PHP设计模式之迭代器模式
2016/06/17 PHP
php基于自定义函数记录log日志方法
2017/07/21 PHP
ThinkPHP 5.1 跨域配置方法
2019/10/11 PHP
php数组指针函数功能及用法示例
2020/02/11 PHP
PHP之header函数详解
2021/03/02 PHP
禁止刷新,回退的JS
2006/11/25 Javascript
不同的jQuery API来处理不同的浏览器事件
2012/12/09 Javascript
jquery队列queue与原生模仿其实现方法分享
2014/03/25 Javascript
JavaScript检查某个function是否是原生代码的方法
2014/08/20 Javascript
jQuery+CSS3+Html5实现弹出层效果实例代码(附源码下载)
2016/05/16 Javascript
Ionic实现页面下拉刷新(ion-refresher)功能代码
2016/06/03 Javascript
Javascript实现图片不间断滚动的代码
2016/06/22 Javascript
浅谈JavaScript中promise的使用
2017/01/11 Javascript
JavaScript之Map和Set_动力节点Java学院整理
2017/06/29 Javascript
在vue 中使用 less的教程详解
2018/09/26 Javascript
Node.js中的不安全跳转如何防御详解
2018/10/21 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
JavaScript判断浏览器运行环境的详细方法
2019/06/30 Javascript
js实现图片区域可点击大小随意改变(适用移动端)代码实例
2019/09/11 Javascript
解决Vue 给mapState中定义的属性赋值报错的问题
2020/06/22 Javascript
Python实现基于HTTP文件传输实例
2014/11/08 Python
python常用知识梳理(必看篇)
2017/03/23 Python
django 发送手机验证码的示例代码
2018/04/25 Python
python调用支付宝支付接口流程
2019/08/15 Python
Python操作列表常用方法实例小结【创建、遍历、统计、切片等】
2019/10/25 Python
CSS3实现鼠标悬停显示扩展内容
2016/08/24 HTML / CSS
使用纯HTML5编写一款网页上的时钟的代码分享
2015/11/16 HTML / CSS
CSMA/CD介质访问控制协议
2015/11/17 面试题
好书伴我成长演讲稿
2014/05/14 职场文书
万能检讨书2000字
2014/10/17 职场文书
2015年入党积极分子评语
2015/03/26 职场文书
Django使用echarts进行可视化展示的实践
2021/06/10 Python
Spring Data JPA使用JPQL与原生SQL进行查询的操作
2021/06/15 Java/Android
Go语言读取txt文档的操作方法
2022/01/22 Golang