最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2019年02月05日 09:29:24   作者:Jeff.Zhong  
這篇文章主要介紹了基于Webpack4和React hooks搭建項目的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

面對日新月異的前端,我表示快學(xué)不動了:joy:。 Webpack 老早就已經(jīng)更新到了 V4.x,前段時間 React 又推出了 hooks API。剛好春節(jié)在家里休假,時間比較空閑,還是趕緊把 React 技術(shù)棧這塊補(bǔ)上。

網(wǎng)上有很多介紹 hooks 知識點(diǎn)的文章,但都比較零碎,基本只能寫一些小 Demo 。還沒有比較系統(tǒng)的,全新的基于 hooks 進(jìn)行搭建實際項目的講解。所以這里就從開發(fā)實際項目的角度,搭建起單頁面 Web App 項目的基本腳手架,并基于 hooks API 實現(xiàn)一個 react 項目模版。

Hooks最吸引人的地方就是用 函數(shù)式組件 代替面向?qū)ο蟮?類組件 。此前的 react 如果涉及到狀態(tài),解決方案通常只能使用 類組件 ,業(yè)務(wù)邏輯一復(fù)雜就容易導(dǎo)致組件臃腫,模塊的解藕也是個問題。而使用基于 hooks 的 函數(shù)組件 后,代碼不僅更加簡潔,寫起來更爽,而且模塊復(fù)用也方便得多,非常看好它的未來。

webpack 4 的配置

沒有使用 create-react-app 這個腳手架,而是從頭開始配置開發(fā)環(huán)境,因為這樣自定義配置某些功能會更方便些。下面這個是通用的配置 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']
      },
      {  /* 
        當(dāng)文件體積小于 limit 時,url-loader 把文件轉(zhuǎn)為 Data URI 的格式內(nèi)聯(lián)到引用的地方
        當(dāng)文件大于 limit 時,url-loader 會調(diào)用 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 文件,配置出開發(fā)環(huán)境的 webpack.dev.js 文件,主要就是啟動開發(fā)服務(wù)器。

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 打包時就會自動壓縮優(yōu)化代碼。

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(默認(rèn) className 屬性),同時也支持局部css module( styleName 屬性),還支持css預(yù)編譯器,這里使用的是 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 項目

下面是項目基本的目錄樹結(jié)構(gòu),接著從入口開始一步步細(xì)化整個項目。

├ package.json
├ src
│ ├ component // 組件目錄
│ ├ reducer  // reducer目錄
│ ├ action.js
│ ├ constants.js
│ ├ context.js
│ └ index.js
├ public // 靜態(tài)文件目錄
│ ├ css
│ └ index.html
├ .babelrc
├ webpack.common.js
├ webpack.dev.js
└ webpack.prod.js

狀態(tài)管理組件使用 redux , react-router 用于構(gòu)建單頁面的項目,因為使用了 hooks API,所以不再需要 react-redux 連接狀態(tài) 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

最大區(qū)別的地方就是 component 組件,基于 函數(shù)式 ,內(nèi)部的表達(dá)式就像是即插即用的插槽,可以很方便的抽取出通用的組件,然后從外部引用。相比之前的 面向?qū)ο?方式,我覺得 函數(shù)表達(dá)式 更受前端開發(fā)者歡迎。

  • useContext 獲取全局的 state
  • useRef 代替之前的 ref
  • useState 代替之前的 state
  • useEffect則可以代替之前的生命周期鉤子函數(shù)
//監(jiān)控數(shù)組中的參數(shù),一旦變化就執(zhí)行
useEffect(() => { updateData(); },[id]);

//不傳第二個參數(shù)的話,它就等價于每次componentDidMount和componentDidUpdate時執(zhí)行
useEffect(() => { updateData(); });

//第二個參數(shù)傳空數(shù)組,等價于只在componentDidMount和componentWillUnMount時執(zhí)行, 
//第一個參數(shù)中的返回函數(shù)用于執(zhí)行清理功能
useEffect(() => { 
  initData(); 
  reutrn () => console.log('componentWillUnMount cleanup...'); 
}, []);

最后就是實現(xiàn)具體界面和業(yè)務(wù)邏輯的組件了,下面是其中的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);//獲取全局狀態(tài)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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論

闸北区| 抚州市| 龙川县| 利津县| 会昌县| 大渡口区| 清丰县| 鄂尔多斯市| 凉山| 固原市| 南汇区| 呼玛县| 连云港市| 吉林省| 汝南县| 卓资县| 石景山区| 南投县| 石门县| 威远县| 获嘉县| 青州市| 新闻| 肥东县| 凤山市| 房产| 南昌县| 文登市| 鄂托克前旗| 巴南区| 犍为县| 高雄市| 蒲城县| 农安县| 洛宁县| 林州市| 富蕴县| 泸西县| 北碚区| 手游| 河南省|