基于 Webpack 4 和 React hooks 搭建项目

面对日新月异的前端,我表示快学不动了??。 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

原文地址:https://www.cnblogs.com/edwardloveyou/p/10351697.html

时间: 2024-10-07 17:41:22

基于 Webpack 4 和 React hooks 搭建项目的相关文章

webpack+babel+ES6+react环境搭建

webpack+babel+ES6+react环境搭建 步骤: 1 创建项目结构 注意: 先创建一个项目目录  react  这个名字自定义,然后进入到这个目录下面 mkdir app //创建app目录 用来存放项目源文件 mkdir dist // 创建dist目录 用来存放打包好的文件 touch .gitignore //创建.gitignore 用来添加git 忽略的文件 touch webpack.config.js //创建webpack的配置文件 cd app //进入到app目

webpack使用原生js和react分别搭建项目

原生js:a. head.jsx: function head(){ var head = document.createElement('div') head.setAttribute('class','head') head.innerHTML = "head" return head } module.exports = head b. table.jsx: function table(){ var table = document.createElement('table')

基于webpack的vue开发环境搭建

1.新建并初始化项目(npm int -y),安装webpack,webpack-cli webpack-dev-server 2.安装vue: npm i vue --save 3.dist下建立index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" con

基于maven的springMvc+Mybatis,搭建项目的时候发现一些问题这里例举下

1.怎样使用maven的tomcat去运行项目? 需要在运行项目的pom文件的build里做如下配置 <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <server>tomca

基于webpack搭建vue-cli以及webstorm的设置

N1.检查本地电脑是否安装node和npm 安装vue-cli的前提是已经安装了npm 是否安装npm检查命令 npm -v 如果出现版本号,说明已经安装了npm 查看node版本号 node -v 如果未出现上述情况,请前往官网下载稳定版本,也可以点右边的穿梭机下载,目前vision用的node版本是 8.9.3 走你--node下载 注意:可以直接在官网上下载8.9.3版本覆盖你本地的版本,安装之后运行npm -v可能会出现如下错误. 解决方法: 删除目录下"C:\Users\你的用户名\A

基于webpack的Vue.js开发环境快速搭建

1. 安装node node下载地址 2. 安装淘宝 NPM 镜像命令地址 3. 安装vue # 全局安装 vue-cli cnpm install -g vue-cli 4. 创建一个基于 "webpack" 模板的新项目 # 创建一个项目文件夹,在文件夹下shift + 右键,选择在此处打开cmd vue init webpack 5. 安装依赖 # 安装依赖 cnpm install #安装完毕后,在项目路径下多出了依赖包. 6. 运行项目 #运行项目 运行成功,默认8080 端

基于Vue-cli 快速搭建项目

Vue-cli可以快速帮助我们创建一个项目,这是官方给我们提供的脚手架.下面我说一下vue-cli的使用方法. 一.准备工作 在使用vue-cli时,首先需要安装nodejs,npm,其次需全局安装vue和vue-cli 1.nodejs和npm安装方法详见https://www.cnblogs.com/le220/p/8670349.html 2.npm install -g vue 3.npm install -g vue-cli 二.vue-cli快速搭建项目 安装完成后,同时在C:\Us

基于requireJS和Gulp可快速搭建前端项目的脚手架

基于requireJS和Gulp可快速搭建前端项目的脚手架. 项目地址 git clone git@github.com:perfectSymphony/Gulp-cli.git 项目目录 ├── README.md # 项目说明 |—— bin # (在gulpfile文件中使用到)解析layout中的模板html,将完整的html产出到src/html中 ├── dist # 打包生成的项目文件 |—— logs # 监听编译less文件时,打印报错信息, ├── gulpfile.js #

Vue-Router路由Vue-CLI脚手架和模块化开发 之 Vue-CLI 2.x脚手架工具基于webpack simple模板与基于webpack模板构建项目

Vue-CLI 2.x脚手架工具基于webpack simple模板构建项目 vue-cli是一个基于vue.js进行快速开发的完整系统.基于webpack构建,并进行默认配置,可通过插件扩展,可升级: vue-cli帮我们创建基本项目结构,跳过繁琐的项目配置环节,将精力集中在业务上: vue-cli的版本: @vue/cli 3.x版本:点击前往官网 vue-cli 2.x版本:点击前往官网 安装vue-cli $ npm install -g vue-cli 安装成功,查看其版本: 使用we