React+Redux+Webpack构建开发单页应用环境

webpack 基础

安装 webpack

作为全局变量安装

$ npm install -g webpack
 $ webpack -v

作为项目依赖安装

$ npm install webpack --save-dev

webpack 简介

webpack 的配置项主要包括以下几点:

  • entry: 入口,定义要打包的文件
  • output: 出口,定义打包输出的文件;包括路径,文件名,还可能有运行时的访问路径(publicPath)参数
  • module: webpack将所有的资源都看做是模块,而模块就需要加载器;主要定义一些loaders,定义哪些后缀名的文件应该用哪些loader
  • test: 检测哪些文件需要此loader,是一个正则表达式
  • exclude: 忽略哪些文件
  • resolve: 定义能够被打包的文件,文件后缀名
  • plugins: 定义一些额外的插件

Loaders

  • 处理样式:less-loader、style-loader、css-loader,将 less 转成 css
  • 图片处理,url-loader、file-loader、image-webpack-loader, 将图片转换成base64 或者 进行压缩
  • js处理: babel-loader,babel-preset-es2015,babel-preset-react,stage-0,将es6或更高级的代码转成es5的代码

Plugins

  • 代码热替换:HotModuleReplacementPlugin
  • 生成html文件:HtmlWebpackPlugin
  • 报错但不退出webpack进程:NoErrorsPlugin
  • 代码压缩:UglifyJsPlugin
  • 自动打开浏览器: OpenBrowserPlugin
  • 设置环境变量: DefinePlugin

使用 webpack-dev-server 启动服务器

  • webpack.config.***.js: webpack常规配置,配置入口文件,输出文件,loaders等等
  • server.js: 将server部分分离到一个单独到的文件配置
  • package.json: 自定义启动命令
<!-- webpack.config.dev.js -->

const webpack = require('webpack');
const path = require('path');
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const DashboardPlugin = require('webpack-dashboard/plugin');
const configBase = require('./config.base')
const ip = require('ip')
const lostIp = ip.address()
const config = {
  entry: [
    'webpack/hot/dev-server','webpack-dev-server/client?http://' + lostIp + ':' + configBase.port,path.resolve(__dirname,configBase.project + '/index.js')
  ],output: {
    filename: 'bundle.js',path: path.resolve(__dirname,'build'),publicPath: 'http://' + lostIp + ':' + configBase.port + '/' // 引用路径
  },resolve: {
    extensions: [ '','.js','.jsx' ]
  },devtool: "source-map",module: {
    loaders: [
      {
        test: /\.js|jsx$/,// 正则匹配
        exclude: /(node_modules|bower_components)/,loaders: [ 'babel','eslint-loader']  // 加载模块 "babel" 是 "babel-loader" 的缩写  eslint 语法检查
      },{
        test: /\.css$/,loaders: [ "style","css?sourceMap" ]
      },{
        test: /\.(woff|woff2|eot|ttf)$/i,loader: "file-loader?name=fonts/[name]-[hash].[ext]"
      },{
        test: /\.less$/,loader: "style!css!less"
      },{
        test: /\.(jpe?g|png|gif|svg|ico)$/i,loaders: [
          'url?limit=10000&name=img/[hash:8].[name].[ext]',// 图片小于8k就转化为 base64,或者单独作为文件
          'image-webpack' // 图片压缩
        ]
      }
    ]
  },eslint: {
    configFile: './.eslintrc'  // eslint配置文件
  },plugins: [
    new HtmlWebpackPlugin({
      favicon: configBase.project + '/favicon.ico',template: configBase.project + '/template.html',filename: 'index.html',inject: true,//允许插件修改哪些内容,包括head与body
      hash: true,//为静态资源生成hash值
      minify: {    //压缩HTML文件
        removeComments: true,//移除HTML中的注释
        collapseWhitespace: true    //删除空白符与换行符
      }
    }),new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development'),__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
    }),new webpack.HotModuleReplacementPlugin(),new webpack.NoErrorsPlugin(),new OpenBrowserPlugin({ url: 'http://' + lostIp + ':' + configBase.port }),new DashboardPlugin()
  ]
}

module.exports = config
<!-- server.js -->

const webpack = require('webpack');
const express = require('express')
const webpackDevServer = require('webpack-dev-server');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const proxy = require('proxy-middleware')
const devConfig = require('./webpack.config.dev.js');
const configBase = require('./config.base')

const port = configBase.port;

const ip = require('ip')
const lostIp = ip.address()
// const app = express()
// const compiler = webpack(devConfig);

// app.use(webpackDevMiddleware(compiler,{
//     publicPath: devConfig.output.publicPath,//     stats: { colors: true },// 用颜色标识
//   }
// ))
// app.use(webpackHotMiddleware(compiler));
// app.use(proxy(configBase.proxyIp));
// app.listen(port,lostIp,function (err) {
//   if (err) {
//     console.log(err);
//   }
//   console.log('------>服务启动中 ' + lostIp + ' port ' + port + '<------');
// });
//
// app.get('/',function (req,res) {
//   console.log(req.url);
//   // res.sendFile(path.join(__dirname,'./dist/index.html'));
// });
//
// app.get('/api','./dist/index.html'));
// });

function baseConfig (config,contentBase) {
  return new webpackDevServer(webpack(config),{
      historyApiFallback: true,//启用历史API后备支持
      hot: true,//添加HotModuleReplacementPlugin和切换服务器热模式
      inline: true,//嵌入的WebPack-dev的服务器运行到包
      progress: true,//显示某种进度条
      contentBase: contentBase,//为内容的基本路径
      stats: { colors: true },// 用颜色标识
    }
  );
}

var server
server = baseConfig(devConfig,"/" + configBase.project);
console.log("开发环境 development mode... " + '项目 ' + configBase.project);

server.listen(port,function (err) {
  if (err) {
    console.log(err);
  }
  console.log('------>服务启动中 ' + lostIp + ' on ' + process.env.NODE_ENV + ' port ' + port + '<------');
});

配置 React,ES6 & Babel 6

ES6 和 JSX 转换 .babelrc 文件

{
  "presets": [
    "es2015","stage-0","react"
  ],"plugins": []
}

使用 ESlint 进行代码检查

特点:

  • 默认规则包含所有 JSLint、JSHint 中存在的规则,易迁移
  • 规则可配置性高:可设置「警告」、「错误」两个 error 等级,或者直接禁用

.eslint配置文件常见的格式

{
  "parserOptions": { //EsLint通过parserOptions,允许指定校验的ecma的版本,及ecma的一些特性
    "ecmaVersion": 6,//指定ECMAScript支持的版本,6为ES6
    "sourceType": "module",//指定来源的类型,有两种”script”或”module”
    "ecmaFeatures": { // ecmaFeatures指定你想使用哪些额外的语言特性
        "jsx": true //启动JSX
    }
  },"parser": "babel-eslint",// EsLint默认使用esprima做脚本解析,也可以切换成babel-eslint解析
  "env": { // Environment可以预设好的其他环境的全局变量,如brower、node环境变量、es6环境变量、mocha环境变量等
    "browser": true,"node": true,"es6": true,"mocha": true
  },"plugins": [ // EsLint允许使用第三方插件
    "react"
  ],extends: [ // Extends是EsLint默认推荐的验证你可以使用配置选择哪些校验是你所需要的
    "eslint:recommended"
  ],rules: [ // 自定义规则
  ],"globals": { // 即插件在执行过程中用到的其它全局变量
  }
}

相关插件

  • babel-eslint: ESLint 是前端JS代码检测利器。而 babel-eslint 则允许你检测所有的 Babel 代码
  • eslint: JavaScript 语法检测利器:分析出你代码潜在的错误和非标准用法
  • eslint-plugin-react: ESLint 中关于 React 语法检测的插件

更多参考

Configuration tasks/命令

  • npm start: 启动开发模式下的server
  • npm run build: 打包生产模式的代码
  • npm run lint⌚️eslint 监视

参考

React官网

WebPack实例与前端性能优化

React 入门实例教程

git仓库地址:https://github.com/zdliuccit/reactProject

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom