亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

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

2024-05-06 16:47:59
字體:
來源:轉載
供稿:網友

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

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

Hooks最吸引人的地方就是用 函數式組件 代替面向對象的 類組件 。此前的 react 如果涉及到狀態,解決方案通常只能使用 類組件 ,業務邏輯一復雜就容易導致組件臃腫,模塊的解藕也是個問題。而使用基于 hooks 的 函數組件 后,代碼不僅更加簡潔,寫起來更爽,而且模塊復用也方便得多,非??春盟奈磥怼?/p>

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.jsimport 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.jsexport const ADD_COMMENT = 'ADD_COMMENT'export const REMOVE_COMMENT = 'REMOVE_COMMENT'// action.jsimport { 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.jsimport 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"          setVisible(true);          setRid(l.id);        }}></i></li>)      } </ul>      <input ref={inputRef} type="text" />      <button onClick={add}      <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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产91精品高潮白浆喷水| 精品久久久在线观看| 久久精品精品电影网| 亚洲精品一二区| 国产mv久久久| 久久久久久久久久久亚洲| 欧美激情一级二级| 亚洲国产高清高潮精品美女| 国产精品永久免费视频| 疯狂欧美牲乱大交777| 国产+人+亚洲| 福利视频导航一区| 最新69国产成人精品视频免费| 2018日韩中文字幕| 91精品国产高清久久久久久91| 日韩欧美在线字幕| 亚洲成人a级网| 国产欧美日韩视频| 热99精品里视频精品| 亚洲精品久久久久中文字幕欢迎你| 69国产精品成人在线播放| 免费不卡在线观看av| 亚洲人成绝费网站色www| 综合国产在线视频| 日韩视频免费中文字幕| 欧美国产日韩中文字幕在线| 亚州国产精品久久久| 91久久精品国产91久久性色| 久久久久久久久久久免费精品| 亚洲综合国产精品| 国产精自产拍久久久久久蜜| 亚洲精品国产综合久久| 亚洲一区二区久久久| 亚洲jizzjizz日本少妇| 国产免费一区二区三区香蕉精| 欧美综合国产精品久久丁香| 亚洲欧美一区二区三区情侣bbw| 欧美电影免费观看网站| 欧美激情亚洲国产| 国产日韩中文在线| 在线中文字幕日韩| 亚洲精品国产拍免费91在线| 日韩黄色高清视频| 亚洲男人7777| 91美女福利视频高清| 亚洲欧洲日本专区| 欧美精品久久久久久久久| 狠狠躁夜夜躁久久躁别揉| 国产精品pans私拍| 精品激情国产视频| 国产精品情侣自拍| 欧美亚洲视频在线看网址| 日韩亚洲欧美成人| 亚洲欧美中文日韩在线v日本| 自拍视频国产精品| 国产精品高潮呻吟视频| 亚洲欧美激情四射在线日| 狠狠色狠色综合曰曰| 成人午夜高潮视频| 欧美性在线视频| 午夜精品99久久免费| 亚洲激情自拍图| 欧美极品少妇全裸体| 欧美性做爰毛片| 秋霞av国产精品一区| 色偷偷888欧美精品久久久| 久久视频免费在线播放| 国产大片精品免费永久看nba| 精品久久久久久| 91色视频在线观看| 日韩美女视频免费看| 中文字幕亚洲欧美日韩在线不卡| 欧美影院在线播放| 久久久久久亚洲精品中文字幕| 成人乱人伦精品视频在线观看| 欧美成在线视频| 亚洲电影成人av99爱色| 国产精品偷伦免费视频观看的| 国产精品jizz在线观看麻豆| 成人国产精品日本在线| 亚洲国产精品福利| 亚洲欧洲国产伦综合| 亚洲人在线视频| 欧美老少配视频| 成人久久久久久| 久久久久中文字幕2018| 国产精品午夜一区二区欲梦| 亚洲国产成人久久综合| 日本最新高清不卡中文字幕| 日韩欧美在线网址| www.日韩不卡电影av| 日韩欧美999| 国产成人涩涩涩视频在线观看| 国产精品成人播放| 久久亚洲成人精品| 国产成人在线一区| 亚洲有声小说3d| 久久久视频免费观看| 欧美另类老肥妇| 亚洲最新在线视频| 国产一区二区三区直播精品电影| 久久精品99无色码中文字幕| 自拍偷拍亚洲区| 久久av在线看| 日韩精品福利在线| 欧美在线视频一区| 亚洲精品久久久久久久久久久| 成人h视频在线观看播放| 国产精品久久久久7777婷婷| 97视频免费在线看| 国产剧情日韩欧美| 欧美日韩一区二区三区| 色小说视频一区| 日本欧美在线视频| 国产69精品久久久| 精品高清美女精品国产区| 精品国产依人香蕉在线精品| 欧美性xxxxhd| 色阁综合伊人av| 欧美性生交大片免网| 欧美成人精品在线| 国产香蕉一区二区三区在线视频| 日韩中文字幕视频在线观看| 日韩中文字幕不卡视频| 欧美日韩国产专区| 91久久嫩草影院一区二区| 国产ts人妖一区二区三区| 亚州成人av在线| 国产精品久久久久久中文字| 亚洲第一二三四五区| 国产精品美乳一区二区免费| 亚洲免费视频观看| 日韩欧美亚洲范冰冰与中字| 欧美性猛交丰臀xxxxx网站| 亚洲乱码国产乱码精品精| 91禁国产网站| 亚洲成人久久久久| 97精品国产97久久久久久免费| 国产成人精品久久久| 91中文精品字幕在线视频| 91大神在线播放精品| 91久久精品日日躁夜夜躁国产| 久久在线视频在线| 久久久国产在线视频| 欧美一区二区三区图| 国产ts人妖一区二区三区| xxxxx91麻豆| 久久精品国产99国产精品澳门| 日本精品久久电影| 曰本色欧美视频在线| 国产精品爽爽ⅴa在线观看| 国产精品入口日韩视频大尺度| 中日韩午夜理伦电影免费| 日韩精品在线播放| 成人欧美一区二区三区在线| 日韩美女视频免费在线观看| 成人两性免费视频| 国产精品视频导航| 亚洲天堂av女优| 亚洲一品av免费观看| 久久色精品视频| 日韩电影中文字幕在线| 91精品久久久久久久久中文字幕| 精品亚洲永久免费精品|