基于Webpack4和React hooks搭建项目的方法

面对日新月异的前端,我表示快学不动了:joy:。 Webpack 老早就已经更新到了 V4.x,前段时间 React 又推出了 hooks API。刚好春节在家里休假,时间比较空闲,还是赶紧把 React 技术栈这块补上。

网上有很多介绍 hooks 知识点的文章,但都比较零碎,基本只能写一些小 Demo 。还没有比较系统的,全新的基于 hooks 进行搭建实际项目的讲解。所以这里就从开发实际项目的角度,搭建起单页面 Web App 项目的基本脚手架,并基于 hooks API 实现一个 react 项目模版。

Hooks最吸引人的地方就是用 函数式组件 代替面向对象的 类组件 。此前的 react 如果涉及到状态,解决方案通常只能使用 类组件 ,业务逻辑一复杂就容易导致组件臃肿,模块的解藕也是个问题。而使用基于 hooks 的 函数组件 后,代码不仅更加简洁,写起来更爽,而且模块复用也方便得多,非常看好它的未来。

webpack 4 的配置

没有使用 create-react-app 这个脚手架,而是从头开始配置开发环境,因为这样自定义配置某些功能会更方便些。下面这个是通用的配置 webpack.common.js 文件。

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const { HotModuleReplacementPlugin } = require('webpack');

module.exports = {
  entry: './src/index.js',//单入口
  output: {
    path: resolve(__dirname, 'dist'),
    filename: '[name].[hash].js'//输出文件添加hash
  },
  optimization: { // 代替commonchunk, 代码分割
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.scss$/,
        use: ['style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,//css modules
              localIdentName: '[name]___[local]___[hash:base64:5]'
            },
          },
          'postcss-loader', 'sass-loader']
      },
      {  /*
        当文件体积小于 limit 时,url-loader 把文件转为 Data URI 的格式内联到引用的地方
        当文件大于 limit 时,url-loader 会调用 file-loader, 把文件储存到输出目录,并把引用的文件路径改写成输出后的路径
        */
        test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 1000
          }
        }]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),//生成新文件时,清空生出目录
    new HtmlWebpackPlugin({
      template: './public/index.html',//模版路径
      favicon: './public/favicon.png',
      minify: { //压缩
        removeAttributeQuotes:true,
        removeComments: true,
        collapseWhitespace: true,
        removeScriptTypeAttributes:true,
        removeStyleLinkTypeAttributes:true
       },
    }),
    new HotModuleReplacementPlugin()//HMR
  ]
};

接着基于 webpack.common.js 文件,配置出开发环境的 webpack.dev.js 文件,主要就是启动开发服务器。

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
    port: 4001,
    hot: true
  }
});

生成模式的 webpack.prod.js 文件,只要定义了 mode:'production' , webpack 4 打包时就会自动压缩优化代码。

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
 mode: 'production',
 devtool: 'source-map'
});

配置 package.js 中的 scripts

{
 "scripts": {
   "start": "webpack-dev-server --open --config webpack.dev.js",
   "build": "webpack --config webpack.prod.js"
 }
}

Babel 的配置

babel的 .babelrc 文件, css module 包这里推荐 babel-plugin-react-css-modules

react-css-modules既支持全局的css(默认 className 属性),同时也支持局部css module( styleName 属性),还支持css预编译器,这里使用的是 scss 。

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime",
    [
      "react-css-modules",
      {
        "exclude": "node_modules",
        "filetypes": {
          ".scss": {
            "syntax": "postcss-scss"
          }
        },
        "generateScopedName": "[name]___[local]___[hash:base64:5]"
      }
    ]
  ]
}

React 项目

下面是项目基本的目录树结构,接着从入口开始一步步细化整个项目。

├ package.json
├ src
│ ├ component // 组件目录
│ ├ reducer  // reducer目录
│ ├ action.js
│ ├ constants.js
│ ├ context.js
│ └ index.js
├ public // 静态文件目录
│ ├ css
│ └ index.html
├ .babelrc
├ webpack.common.js
├ webpack.dev.js
└ webpack.prod.js

状态管理组件使用 redux , react-router 用于构建单页面的项目,因为使用了 hooks API,所以不再需要 react-redux 连接状态 state 。

<Context.Provider value={{ state, dispatch }}>基本代替了 react-redux 的 ** `。

// index.js
import React, { useReducer } from 'react'
import { render } from 'react-dom'
import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom'
import Context from './context.js'
import Home from './component/home.js'
import List from './component/list.js'
import rootReducer from './reducer'
import '../public/css/index.css'

const Root = () => {
  const initState = {
    list: [
      { id: 0, txt: 'webpack 4' },
      { id: 1, txt: 'react' },
      { id: 2, txt: 'redux' },
    ]
  };
  // useReducer映射出state,dispatch
  const [state, dispatch] = useReducer(rootReducer, initState);
  return <Context.Provider value={{ state, dispatch }}>
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/list" component={List} />
        <Route render={() => (<Redirect to="/" />)} />
      </Switch>
    </Router>
  </Context.Provider>
}
render(
  <Root />,
  document.getElementById('root')
)

constants.js, action.js 和 reducer.js 与之前的写法是一致的。

// constants.js
export const ADD_COMMENT = 'ADD_COMMENT'
export const REMOVE_COMMENT = 'REMOVE_COMMENT'
// action.js
import { ADD_COMMENT, REMOVE_COMMENT } from './constants'

export function addComment(comment) {
 return {
  type: ADD_COMMENT,
  comment
 }
}

export function removeComment(id) {
 return {
  type: REMOVE_COMMENT,
  id
 }
}

list.js

import { ADD_COMMENT, REMOVE_COMMENT } from '../constants.js'

const list = (state = [], payload) => {
  switch (payload.type) {
    case ADD_COMMENT:
      if (Array.isArray(payload.comment)) {
        return [...state, ...payload.comment];
      } else {
        return [...state, payload.comment];
      }
    case REMOVE_COMMENT:
      return state.filter(i => i.id != payload.id);
    default: return state;
  }
};
export default list

reducer.js

import { combineReducers } from 'redux'
import list from './list.js'

const rootReducer = combineReducers({
 list,
 //user
});

export default rootReducer

最大区别的地方就是 component 组件,基于 函数式 ,内部的表达式就像是即插即用的插槽,可以很方便的抽取出通用的组件,然后从外部引用。相比之前的 面向对象 方式,我觉得 函数表达式 更受前端开发者欢迎。

  • useContext 获取全局的 state
  • useRef 代替之前的 ref
  • useState 代替之前的 state
  • useEffect则可以代替之前的生命周期钩子函数
//监控数组中的参数,一旦变化就执行
useEffect(() => { updateData(); },[id]);

//不传第二个参数的话,它就等价于每次componentDidMount和componentDidUpdate时执行
useEffect(() => { updateData(); });

//第二个参数传空数组,等价于只在componentDidMount和componentWillUnMount时执行,
//第一个参数中的返回函数用于执行清理功能
useEffect(() => {
  initData();
  reutrn () => console.log('componentWillUnMount cleanup...');
}, []);

最后就是实现具体界面和业务逻辑的组件了,下面是其中的List组件

// list.js
import React, { useRef, useState, useContext } from 'react'
import { bindActionCreators } from 'redux'
import { Link } from 'react-router-dom'
import Context from '../context.js'
import * as actions from '../action.js'
import Dialog from './dialog.js'
import './list.scss'

const List = () => {
  const ctx = useContext(Context);//获取全局状态state
  const { user, list } = ctx.state;
  const [visible, setVisible] = useState(false);
  const [rid, setRid] = useState('');
  const inputRef = useRef(null);
  const { removeComment, addComment } = bindActionCreators(actions, ctx.dispatch);

  const confirmHandle = () => {
    setVisible(false);
    removeComment(rid);
  }

  const cancelHandle = () => {
    setVisible(false);
  }

  const add = () => {
    const input = inputRef.current,
      val = input.value.trim();
    if (!val) return;
    addComment({
      id: Math.round(Math.random() * 1000000),
      txt: val
    });
    input.value = '';
  }

  return <>
    <div styleName="form">
      <h3 styleName="sub-title">This is list page</h3>
      <div>
        <p>hello, {user.name} !</p>
        <p>your email is {user.email} !</p>
        <p styleName="tip">please add and remove the list item !!</p>
      </div>
      <ul> {
        list.map(l => <li key={l.id}>{l.txt}<i className="icon-minus" title="remove item" onClick={() => {
          setVisible(true);
          setRid(l.id);
        }}></i></li>)
      } </ul>
      <input ref={inputRef} type="text" />
      <button onClick={add} title="add item">Add Item</button>
      <Link styleName="link" to="/">redirect to home</Link>
    </div>
    <Dialog visible={visible} confirm={confirmHandle} cancel={cancelHandle}>remove this item ?</Dialog>
  </>
}

export default List;

项目代码

https://github.com/edwardzhong/webpack_react

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

(0)

相关推荐

  • 基于Webpack4和React hooks搭建项目的方法

    面对日新月异的前端,我表示快学不动了:joy:. Webpack 老早就已经更新到了 V4.x,前段时间 React 又推出了 hooks API.刚好春节在家里休假,时间比较空闲,还是赶紧把 React 技术栈这块补上. 网上有很多介绍 hooks 知识点的文章,但都比较零碎,基本只能写一些小 Demo .还没有比较系统的,全新的基于 hooks 进行搭建实际项目的讲解.所以这里就从开发实际项目的角度,搭建起单页面 Web App 项目的基本脚手架,并基于 hooks API 实现一个 rea

  • 手把手教你从零开始react+antd搭建项目

    之前的文章都是自己的学习日志,主要是防止自己遗忘之前遇到的坑.这次将从最基础的项目搭建开始讲起,做一个基于react和antd的后台管理系统.我会一步步进行下去,所以看完本文你哪怕不了解react,应该也会使用react做一个简单的项目.话不多少,直接开始.完整项目请前往GitHub查看,体验请点击这里.如果觉得可以请给一颗star,谢谢各位. 1.开发环境: node.js -v 12.16.3 create-react-app -v 3.4.1 antd -v 4.3.3 项目开始前请自行全

  • 基于Vue和Element-Ui搭建项目的方法

    首先要求事先安装node和npm 没有安装的自行百度或在论坛里面搜索! 提示:在命令行分别输入node -v(node和-v之间有个空格) 和npm -v(同样有个空格)可查看当前的node和npm版本 创建vue项目 1.创建一个项目文件夹,记住文件夹路径,如我的是F:\AppCode 2.打开cmd命令通过cd指令进入到刚才创建的文件夹路径F:\AppCode. 输入npm install -g cnpm –registry=https://registry.npm.taobao.org安装

  • Zend Framework基于Command命令行建立ZF项目的方法

    本文实例讲述了Zend Framework基于Command命令行建立ZF项目的方法.分享给大家供大家参考,具体如下: zend framework 的项目结构比较复杂,但是有既定的结构.zf提供了使用Command生成项目结构的工具,使用非常方便,初学者可以不用为了复杂的结构而Orz. 使用前的一些配置. 涉及到的文件: 1.zf 的 library 2.bin zf下载时所带的bin文件夹 3.php.exe 第一步: 将library和bin文件夹拷贝到服务器根目录,我的服务器跟目录为E:

  • React Hooks获取数据实现方法介绍

    目录 前言 useState useEffect 获取数据 前言 react16.8推出hooks更好的支持函数组件,使用函数组件更容易进行代码的复用,拓展性更强. useState useState类似于class组件的state功能,用于更新视图 import React, { Component, useState } from 'react'; export default function Hello() { const [count, setCount] = useState(0);

  • 基于webpack4.X从零搭建React脚手架的方法步骤

    项目初始化 $ npm init 安装webpack 本次创建是基于webpack4 $ npm install --save-dev 新建webpack配置文件 在根目录创建build文件夹,添加一个js文件,命名为webpack.base.conf.js // webpack.base.conf.js 文件 const path = require('path'); const DIST_PATH = path.resolve(__dirname, '../dist'); module.ex

  • 使用vue-cli(vue脚手架)快速搭建项目的方法

    vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目.这篇文章将会从实操的角度,介绍整个搭建的过程. 1. 避坑前言 其实这次使用vue-cli的过程并不顺利,反复尝试几次都遇到以下这个报错: 创建vue-cli工程项目时的报错 在网上查了很多资料才发现原来是node版本过低的问题,虽然没有找到官方对这个"过低"问题的解释,但是根据国外网友的经验之谈,应该是至少使用node6,我将node4更新至node8之后确实没有报错了,顺利搭

  • 如何对react hooks进行单元测试的方法

    写在前面 使用 react hook 来做公司的新项目有一段时间了,大大小小的坑踩了不少.由于是公司项目,因此必须要编写单元测试来确保业务逻辑的正确性以及重构时代码的可维护性与稳定性,之前的项目使用的是 react@15.x 的版本,使用 enzyme 配合 jest 来做单元测试毫无压力,但新项目使用的是 react@16.8 ,编写单元测试的时候,遇到不少阻碍,因此总结此篇文章算作心得分享出来. 配合 enzyme 来进行测试 首先,enzyme 对于 hook 的支持程度,可以参考这个 i

  • Linux基于php-fpm模式的lamp搭建phpmyadmin的方法

    一. 名词简介 1.linux Linux 是免费开源软件,这意味着源代码可用的操作系统. 2. lamp lamp指的Linux(操作系统).ApacheHTTP 服务器,MySQL(有时也指MariaDB,数据库软件) 和PHP(有时也是指Perl或Python) 的第一个字母,一般用来建立web应用平台. 3.mysql mysql 是多线程.多用户的SQL数据库管理系统. mysql 已由Oracle公司自 2010 年 1 月 27 日通过SUN购买.SUN 最初于 2008 年 2

  • Vite搭建React项目的方法步骤

    前言 日常放鸽,火钳刘明 这是一个基于 vite 搭建的 React 的项目,开发体验非常棒. 创建一个 Vite 项目 yarn create @vitejs/app 如上图,选择了 react-ts 预设模板,如果出现下图一样的工程 yarn // 安装依赖 yarn dev // 启动开发环境 打开浏览器输入http://localhost:3000/#/,如上图所示的话.那么恭喜你,你可以正常开发 React 项目了.完结撒花 如果不行的话,直接看 vite 官网,它比我写的详细 改造工

随机推荐