浅谈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 相关文章推荐
jQuery 开发者应该注意的9个错误
May 03 Javascript
JavaScript判断undefined类型的正确方法
Jun 30 Javascript
页面get请求 中文参数方法乱码问题的快速解决方法
May 31 Javascript
详解Node.js中的事件机制
Sep 22 Javascript
javascript 正则表达式去空行方法
Jan 24 Javascript
浅谈js中的变量名和函数名重名
Feb 13 Javascript
javascript中的面向对象
Mar 30 Javascript
javascript获取指定区间范围随机数的方法
Sep 08 Javascript
详解.vue文件中监听input输入事件(oninput)
Sep 19 Javascript
动手写一个angular版本的Message组件的方法
Dec 16 Javascript
微信小程序实现手势图案锁屏功能
Jan 30 Javascript
微信小程序自定义扫码功能界面的实现代码
Jul 02 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 将excel导入mysql
2009/11/09 PHP
EXTJS FORM HIDDEN TEXTFIELD 赋值 使用value不好用的问题
2011/04/16 Javascript
javascript中String类的subString()方法和slice()方法
2011/05/24 Javascript
js 输出内容到新窗口具体实现代码
2013/05/31 Javascript
JavaScript的模块化:封装(闭包),继承(原型) 介绍
2013/07/22 Javascript
教你如何在 Javascript 文件里使用 .Net MVC Razor 语法
2014/07/23 Javascript
JavaScript类继承及实例化的方法
2015/07/25 Javascript
JS实现超简洁网页title标题跑动闪烁提示效果代码
2015/10/23 Javascript
js滚轮事件兼容性问题需要注意哪些
2016/11/15 Javascript
JS实现图片放大缩小的方法
2017/02/15 Javascript
js正则表达式验证表单【完整版】
2017/03/06 Javascript
详解vue.js的devtools安装
2017/05/26 Javascript
Easyui在treegrid添加控件的实现方法
2017/06/23 Javascript
Angular.JS读取数据库数据调用完整实例
2019/07/02 Javascript
weui上传多图片,压缩,base64编码的示例代码
2020/06/22 Javascript
Python之PyUnit单元测试实例
2014/10/11 Python
bat和python批量重命名文件的实现代码
2016/05/19 Python
使用Python进行AES加密和解密的示例代码
2018/02/02 Python
Python网络编程之TCP套接字简单用法示例
2018/04/09 Python
详解python3中tkinter知识点
2018/06/21 Python
python实现五子棋程序
2020/04/24 Python
Python过滤掉numpy.array中非nan数据实例
2020/06/08 Python
python的dict判断key是否存在的方法
2020/12/09 Python
浅谈css3新单位vw、vh、vmin、vmax的使用详解
2017/12/01 HTML / CSS
详解css position 5种不同的值的用法
2019/07/30 HTML / CSS
英国建筑用品在线:Building Supplies Online(BSO)
2018/04/30 全球购物
Marlies Dekkers内衣法国官方网上商店:国际知名的荷兰内衣品牌
2019/03/18 全球购物
北京泡泡网网络有限公司.net面试题
2012/07/17 面试题
银行求职信
2014/05/31 职场文书
建设工程授权委托书
2014/09/22 职场文书
房屋转让协议书
2014/10/18 职场文书
2015年中个人总结范文
2015/03/10 职场文书
安全教育第一课观后感
2015/06/17 职场文书
Go语言实现Snowflake雪花算法
2021/06/08 Golang
MySQL连表查询分组去重的实现示例
2021/07/01 MySQL
一文搞懂Java中的注解和反射
2022/06/21 Java/Android