webpack4 + react 搭建多页面应用示例


Posted in Javascript onAugust 03, 2018

webpack 升级到4之后还没好好的同步一个可实用的webpack架子,接下来用webpack4来搭建一个简单的react的多界面应用,废话不说 直接撸码

创建工程

$ mkdir demo && cd demo
$ npm init -y

webpack 配置

安装react + babel 依赖

$ npm i -S react@16.3.0 react-dom@16.3.0

$ npm i -D webpack@4.4.1 webpack-cli@2.0.13 webpack-dev-server@3.1.1 webpack-merge@4.1.2 babel-cli@6.26.0 babel-preset-env@1.6.1 babel-preset-react@6.24.1 babel-preset-react-hmre@1.1.1 babel-loader@7.1.4 file-loader@1.1.11 url-loader@1.0.1

webpack.base.conf.js(config -> webpack)

const entry = require("./webpack.entry.conf");
const newEntry = {};
for (let name in entry) {
  newEntry[name] = entry[name][0]
}
let config = {
  entry: newEntry,
  resolve: {
    extensions: [".js", ".json", ".jsx", ".css", ".pcss"],
  }
};
module.exports = config;

webpack.dev.conf.js

const webpack = require('webpack');//引入webpack
const opn = require('opn');//打开浏览器
const merge = require('webpack-merge');//webpack配置文件合并
const path = require("path");
const baseWebpackConfig = require("./webpack.base.conf");//基础配置
const webpackFile = require("./webpack.file.conf");//一些路径配置
const eslintFormatter = require('react-dev-utils/eslintFormatter');

let config = merge(baseWebpackConfig, {
  /*设置开发环境*/
  mode: 'development',
  output: {
    path: path.resolve(webpackFile.devDirectory),
    filename: 'js/[name].js',
    chunkFilename: "js/[name].js",
    publicPath: ''
  },
  optimization: {
    runtimeChunk: {
      name: 'manifest'
    },
    // 包拆分
    splitChunks: {
      cacheGroups: {
        common: {  // 项目的公共组件
          chunks: "initial",
          name: "common",
          minChunks: 2,
          maxInitialRequests: 5,
          minSize: 0
        },
        vendor: {  // 第三方组件
          test: /node_modules/,
          chunks: "initial",
          name: "vendor",
          priority: 10,
          enforce: true
        }
      }
    }
  },
  plugins: [
    /*设置热更新*/
    new webpack.HotModuleReplacementPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: [
          'babel-loader',
          'cache-loader',
        ],
        include: [
          path.resolve(__dirname, "../../app"),
          path.resolve(__dirname, "../../entryBuild")
        ],
        exclude: [
          path.resolve(__dirname, "../../node_modules")
        ],
      },
      {
        test: /\.(css|pcss)$/,
        loader: 'style-loader?sourceMap!css-loader?sourceMap!postcss-loader?sourceMap',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg|swf)$/,
        loader: 'file-loader?name=[name].[ext]&outputPath=' + webpackFile.resource + '/'
      },
      {
        test: /\.(js|jsx)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              // @remove-on-eject-begin
              baseConfig: {
                extends: [require.resolve('eslint-config-react-app')],
              },
              //ignore: false,
              useEslintrc: false,
              // @remove-on-eject-end
            },
            loader: require.resolve('eslint-loader'),
          },
        ],
        include: [
          path.resolve(__dirname, "../../app")
        ],
        exclude: [
          path.resolve(__dirname, "../../node_modules")
        ],
      }
    ]
  },
  /*设置api转发*/
  devServer: {
    host: '0.0.0.0',
    port: 8080,
    hot: true,
    inline: true,
    contentBase: path.resolve(webpackFile.devDirectory),
    historyApiFallback: true,
    disableHostCheck: true,
    proxy: [
      {
        context: ['/api/**', '/u/**'],
        target: 'http://10.8.200.69:8080/',
        secure: false
      }
    ],
    /*打开浏览器 并打开本项目网址*/
    after() {
      opn('http://localhost:' + this.port);
    }
  }
});
module.exports = config;

webpack.prod.conf.js

const path = require('path');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const baseWebpackConfig = require("./webpack.base.conf");
const webpackFile = require('./webpack.file.conf');
const entry = require("./webpack.entry.conf");
const webpackCom = require("./webpack.com.conf");

let config = merge(baseWebpackConfig, {
  /*设置生产环境*/
  mode: 'production',
  output: {
    path: path.resolve(webpackFile.proDirectory),
    filename: 'js/[name].[chunkhash:8].js',
    chunkFilename: "js/[name]-[id].[chunkhash:8].js",
  },
  optimization: {
    //包清单
    runtimeChunk: {
      name: "manifest"
    },
    //拆分公共包
    splitChunks: {
      cacheGroups: {
        common: { //项目公共组件
          chunks: "initial",
          name: "common",
          minChunks: 2,
          maxInitialRequests: 5,
          minSize: 0
        },
        vendor: {  //第三方组件
          test: /node_modules/,
          chunks: "initial",
          name: "vendor",
          priority: 10,
          enforce: true
        }
      }
    }
  },
  plugins: [
    // extract css into its own file
    new ExtractTextPlugin('css/[name].[md5:contenthash:hex:8].css'),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require('cssnano'),
      cssProcessorOptions: {
        discardComments: {removeAll: true},
        // 避免 cssnano 重新计算 z-index
        safe: true
      },
      canPrint: true
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: [
          'babel-loader',
        ],
      },
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(css|pcss)$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader!postcss-loader"
        })
      },
      {
        test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/,
        loader: 'url-loader?limit=8192&name=[name].[hash:8].[ext]&publicPath=' + webpackFile.resourcePrefix + '&outputPath=' + webpackFile.resource + '/'
      },
      {
        test: /\.swf$/,
        loader: 'file?name=js/[name].[ext]'
      }
    ]
  }
});
let pages = entry;
for (let chunkName in pages) {
  let conf = {
    filename: chunkName + '.html',
    template: 'index.html',
    inject: true,
    title: webpackCom.titleFun(chunkName,pages[chunkName][1]),
    minify: {
      removeComments: true,
      collapseWhitespace: true,
      removeAttributeQuotes: true
    },
    chunks: ['manifest', 'vendor', 'common', chunkName],
    hash: false,
    chunksSortMode: 'dependency'
  };
  config.plugins.push(new HtmlWebpackPlugin(conf));
}
/* 清除 dist */
config.plugins.push(new CleanWebpackPlugin([webpackFile.proDirectory], {root: path.resolve(__dirname, '../../'), verbose: true, dry: false}));


/* 拷贝静态资源 */
copyArr.map(function (data) {
  return config.plugins.push(data)
});


module.exports = config;

构建多界面

整体架构搭建起来之后

app -> component

$ mkdir demo && cd demo
$ touch Index.jsx
  import React from 'react';
  class Index extends React.Component {
    render() {
      return (
        <div className="demo">
          写个demo
        </div>
      );
    }
  }
    export default Index;

在config -> entry

module.exports = [
  {
    name: 'index',
    path: 'index/Index.jsx',
    title: '首页',
    keywords: '首页',
    description: '首页'
  },
  {
    name: 'demo',
    path: 'demo/Index.jsx',
    title: 'demo',
    keywords: 'demo',
    description: 'demo'
  },
  {
    name: 'demo1',
    path: 'demo1/Index.jsx',
    title: 'demo1',
    keywords: 'demo1',
    description: 'demo1'
  }
];

然后直接执行 npm run create-dev 就会在devBuild 和 entryBuild 中添加一个新的demo.html 和 demo.js

package.json

{
 "name": "webpack_es6",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
  "dev": "webpack-dev-server --devtool eval --progress --colors --profile --config config/webpack/webpack.dev.conf.js",
  "entry": "node config/entry/entryBuild.js",
  "devBuildHtml": "node config/webpack/webpack.devBuildHtml.conf.js",
  "create-dev": "npm run entry && npm run devBuildHtml",
  "build": "BABEL_ENV=production && webpack --progress --colors --config config/webpack/webpack.prod.conf.js",
  "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
  "react": "^16.3.0",
  "react-dom": "^16.3.0"
 },
 "devDependencies": {
  "babel-cli": "^6.26.0",
  "babel-eslint": "^8.2.2",
  "babel-loader": "^7.1.4",
  "babel-preset-env": "^1.6.1",
  "babel-preset-react": "^6.24.1",
  "babel-preset-react-hmre": "^1.1.1",
  "cache-loader": "^1.2.2",
  "clean-webpack-plugin": "^0.1.19",
  "copy-webpack-plugin": "^4.5.1",
  "css-loader": "^0.28.11",
  "eslint": "^4.19.1",
  "eslint-config-react-app": "^2.1.0",
  "eslint-loader": "^2.0.0",
  "eslint-plugin-flowtype": "^2.46.1",
  "eslint-plugin-import": "^2.10.0",
  "eslint-plugin-jsx-a11y": "^5.1.1",
  "eslint-plugin-react": "^7.7.0",
  "extract-text-webpack-plugin": "^4.0.0-beta.0",
  "file": "^0.2.2",
  "file-loader": "^1.1.11",
  "html-webpack-plugin": "^3.1.0",
  "optimize-css-assets-webpack-plugin": "^4.0.0",
  "postcss-cssnext": "^3.1.0",
  "postcss-loader": "^2.1.3",
  "precss": "^3.1.2",
  "react-dev-utils": "^5.0.0",
  "style-loader": "^0.20.3",
  "url-loader": "^1.0.1",
  "webpack": "^4.4.1",
  "webpack-cli": "^2.0.13",
  "webpack-dev-server": "^3.1.1",
  "webpack-merge": "^4.1.2"
 },
 "eslintConfig": {
  "extends": "react-app",
  "rules": {
   "import/no-webpack-loader-syntax": 0,
   "no-script-url": 0,
   "jsx-a11y/href-no-hash": 2
  }
 }
}

开发环境小技巧

在开发环境添加cache-loader 可以提升在开发环境的编译速度

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

Javascript 相关文章推荐
javascript while语句和do while语句的区别分析
Dec 08 Javascript
简单实用的js调试logger组件实现代码
Nov 20 Javascript
js调用css属性写法
Sep 21 Javascript
一行命令搞定node.js 版本升级
Jul 20 Javascript
移动设备web开发首选框架:zeptojs介绍
Jan 29 Javascript
javascript电商网站抢购倒计时效果实现
Nov 19 Javascript
JS 组件系列之 bootstrap treegrid 组件封装过程
Apr 28 Javascript
jQuery插件FusionCharts绘制的2D双柱状图效果示例【附demo源码】
May 13 jQuery
vue.js整合vux中的上拉加载下拉刷新实例教程
Jan 09 Javascript
vue+iview+less+echarts实战项目总结
Feb 22 Javascript
vue响应式系统之observe、watcher、dep的源码解析
Apr 09 Javascript
Vue强制组件重新渲染的方法讨论
Feb 03 Javascript
使用JS代码实现俄罗斯方块游戏
Aug 03 #Javascript
小程序tab页无法传递参数的方法
Aug 03 #Javascript
详解Webpack多环境代码打包的方法
Aug 03 #Javascript
浅析vue-router jquery和params传参(接收参数)$router $route的区别
Aug 03 #jQuery
浅析Vue 和微信小程序的区别、比较
Aug 03 #Javascript
Vue 项目分环境打包的方法示例
Aug 03 #Javascript
如何在vue里添加好看的lottie动画
Aug 02 #Javascript
You might like
《一拳超人》埼玉一拳下去,他们存在了800年毫无意义!
2020/03/02 日漫
php随机显示图片的简单示例
2014/02/15 PHP
PHP获取数组最后一个值的2种方法
2015/01/21 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
javascript 新浪背投广告实现代码
2009/07/07 Javascript
javascript 构建一个xmlhttp对象池合理创建和使用xmlhttp对象
2010/01/15 Javascript
javascript判断ie浏览器6/7版本加载不同样式表的实现代码
2011/12/26 Javascript
Javascript 中创建自定义对象的方法汇总
2014/12/04 Javascript
AngularJs基本特性解析(一)
2016/07/21 Javascript
jQuery插件echarts实现的循环生成图效果示例【附demo源码下载】
2017/03/04 Javascript
Nodejs基于LRU算法实现的缓存处理操作示例
2017/03/17 NodeJs
js微信应用场景之微信音乐相册案例分享
2017/08/11 Javascript
简单快速的实现js计算器功能
2017/08/17 Javascript
js中json对象和字符串的理解及相互转化操作实现方法
2017/09/22 Javascript
Django与Vue语法的冲突问题完美解决方法
2017/12/14 Javascript
基于Vue实现可以拖拽的树形表格实例详解
2018/10/18 Javascript
antd组件Upload实现自己上传的实现示例
2018/12/18 Javascript
JS如何实现网站中PC端和手机端自动识别并跳转对应的代码
2020/01/08 Javascript
node.js使用zlib模块进行数据压缩和解压操作示例
2020/02/12 Javascript
Vue前端判断数据对象是否为空的实例
2020/09/02 Javascript
js删除对象中的某一个字段的方法实现
2021/01/11 Javascript
virtualenv实现多个版本Python共存
2017/08/21 Python
python爬虫爬取网页表格数据
2018/03/07 Python
python读取和保存视频文件
2018/04/16 Python
详解Python3之数据指纹MD5校验与对比
2019/06/11 Python
关于tf.matmul() 和tf.multiply() 的区别说明
2020/06/18 Python
美国现代家具和家居商店:Apt2B
2016/08/29 全球购物
Stubhub英国:购买体育、演唱会和剧院门票
2018/06/10 全球购物
戴尔马来西亚官网:Dell Malaysia
2020/05/02 全球购物
C#中有没有静态构造函数,如果有是做什么用的?
2016/06/04 面试题
公司门卫的岗位职责
2014/02/19 职场文书
党的群众路线教育实践活动查摆问题及整改措施
2014/10/10 职场文书
房地产销售主管岗位职责
2015/02/13 职场文书
2015年爱牙日活动总结
2015/03/23 职场文书
心灵捕手观后感
2015/06/02 职场文书
MySQL时间盲注的五种延时方法实现
2021/05/18 MySQL