国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

基于Webpack4和React hooks搭建項(xiàng)目的方法

2019-11-19 12:10:24
字體:
供稿:網(wǎng)友

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

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

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

webpack 4 的配置

沒有使用 create-react-app 這個(gè)腳手架,而是從頭開始配置開發(fā)環(huán)境,因?yàn)檫@樣自定義配置某些功能會(huì)更方便些。下面這個(gè)是通用的配置 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 時(shí),url-loader 把文件轉(zhuǎn)為 Data URI 的格式內(nèi)聯(lián)到引用的地方        當(dāng)文件大于 limit 時(shí),url-loader 會(huì)調(diào)用 file-loader, 把文件儲(chǔ)存到輸出目錄,并把引用的文件路徑改寫成輸出后的路徑         */        test: //.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(/?.+)?$/,        use: [{          loader: 'url-loader',          options: {            limit: 1000          }        }]      }    ]  },  plugins: [    new CleanWebpackPlugin(['dist']),//生成新文件時(shí),清空生出目錄    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 文件,主要就是啟動(dòng)開發(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 打包時(shí)就會(huì)自動(dòng)壓縮優(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 屬性),同時(shí)也支持局部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 項(xiàng)目

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

├ 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)建單頁面的項(xiàng)目,因?yàn)槭褂昧?hooks API,所以不再需要 react-redux 連接狀態(tài) 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

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

  • useContext 獲取全局的 state
  • useRef 代替之前的 ref
  • useState 代替之前的 state
  • useEffect則可以代替之前的生命周期鉤子函數(shù)
//監(jiān)控?cái)?shù)組中的參數(shù),一旦變化就執(zhí)行useEffect(() => { updateData(); },[id]);//不傳第二個(gè)參數(shù)的話,它就等價(jià)于每次componentDidMount和componentDidUpdate時(shí)執(zhí)行useEffect(() => { updateData(); });//第二個(gè)參數(shù)傳空數(shù)組,等價(jià)于只在componentDidMount和componentWillUnMount時(shí)執(zhí)行, //第一個(gè)參數(shù)中的返回函數(shù)用于執(zhí)行清理功能useEffect(() => {   initData();   reutrn () => console.log('componentWillUnMount cleanup...'); }, []);

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

項(xiàng)目代碼

https://github.com/edwardzhong/webpack_react

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 隆德县| 白水县| 共和县| 修文县| 长武县| 浙江省| 商丘市| 太白县| 顺义区| 岢岚县| 公主岭市| 扶风县| 仁寿县| 嘉禾县| 正镶白旗| 龙游县| 冕宁县| 珠海市| 宣武区| 鹤山市| 无锡市| 清镇市| 津南区| 平湖市| 建始县| 博乐市| 冀州市| 淮南市| 齐河县| 淮滨县| 土默特左旗| 历史| 彰化市| 藁城市| 汶川县| 天门市| 江北区| 桃园市| 洛扎县| 铁岭县| 上杭县|