react搭建后台管理系統


react搭建后台管理系統

一、項目前期的准備工作

  • 創建git項目並初始化

  • 安裝yarn:npm install yarn -g

  • 安裝node.js

  • yarn init對項目進行初始化,並在命令行根據提示進行必要的設置

"name": "cms-react",
  "version": "1.0.0",
  "main": "index.js",
  "repository": "倉庫地址",
  "author": "git用戶名",
  "license": "MIT",
  "private": true  // 是否是私有倉庫
  • webpack安裝與配置

    • yarn安裝:yarn add webpack@3.10.0 --dev
    • 在項目根目錄創建出口文件webpack.config.js
    const path = require('path');
    const webpack = require('webpack'); // 引入webpack
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    
    module.exports = {
      entry: './path/to/my/entry/file.jsx', // 項目的入口文件路徑
      output: {
        path: path.resolve(__dirname, 'dist'), // 打包后的項目文件路由,一般不用變
        publicPath: '/dist/', // 公共路徑
        filename: 'js/app.js' // 打包后的入口文件名,根據項目實際情況修改
      },
      // babel-loader配置
      module: {
        rules: [
          // react(jsx)文件的處理配置
          {
            test: /\.m?jsx$/,
            exclude: /(node_modules)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env', '@babel/preset-react'],
              }
            }
          },
          // css文件的處理配置
          {
            test: /\.css$/i,
            use: ExtractTextPlugin.extract({
              fallback: "style-loader",
              use: "css-loader"
            })
          },
          // sass文件的處理配置
          {
            test: /\.scss$/i,
            use: ExtractTextPlugin.extract({
              fallback: "style-loader",
              use: ["css-loader", 'sass-loader']
            })
          },
          // url-loader(圖片)的處理配置
          {
            test: /\.(png|jpg|gif)$/i,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 8192, // 如果超過這個值才會單獨生成一個文件
                  name: 'resource/[name].[ext]' // 文件名及擴展
                },
              },
            ]
          },
          // 字體圖標的配置
          {
            test: /\.(eot|svg|ttf|woff|woff2|otf)$/i,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 8192,
                  name: 'resource/[name].[ext]' // 文件名及擴展
                },
              },
            ]
          },
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          // 處理html文件
          template: './src/index.html'  // 項目中自定義模板的路徑設置
        }),
      // 處理css文件
        new ExtractTextPlugin("css/[name].css"),
      // 提出公共模塊
        new webpack.optimize.CommonsChunkPlugin({
        name: "common",
          filename: 'js/base.js'
      })
      ],
    // devServer配置
      devServer: { 
        port: 8086  // 自定義端口號,和主要的軟件服務端口號不能沖突
    },
    }
    
    • 使用webpack打包:項目中webpack沒有全局安裝,那么打包的命令為node_modules/.bin/webpack

    • 安裝插件HtmlWebpackPlugin,在Webpack.config.js配置文件中添加安裝的插件名。安裝命令:npm uninstall html-webpack-plugin npm install html-webpack-plugin@3.2.0 -D,這里注意版本問題,如果版本有問題,在打包的時候會報錯:TypeError: Cannot read property 'make' of undefined,安裝合適的版本即可

    • 安裝插件babel-loader,安裝命令為: yarn add -D babel-loader @babel/core @babel/preset-env,也可以使用yarn add babel-core@版本號安裝多個版本的core

    • 安裝配置react,命令:

      • npm install --save-dev @babel/preset-react
      • yarn add react@16.2.0 react-dom@16.2.0
      • 安裝並配置css插件,命令:yarn add style-loader css-loader --dev
    • 安裝extract-text-webpack-plugin插件,命令:yarn add extract-text-webpack-plugin@3.0.2 --dev。在打包的時候可能會報錯: (node:16920) DeprecationWarning: Tapable.plugin is deprecated. Use new API on .hooks instead,或者報錯:TypeError: Cannot read property 'thisCompilation' of undefined

      • 原因:webpack版本在4.0以上,版本不適配
      • 解決辦法:
        • 先卸載執行 npm uninstall --save-dev extract-text-webpack-plugin
        • 再安裝 npm install --save-dev extract-text-webpack-plugin
    • 安裝sass-loader,命令:yarn add sass-loader@6.0.6 --dev

    • sass-loader生效的前提是需要安裝node-sass, 命令為: yarn add node-sass --dev,這個包和其他的包不同,其他的包都是調用文件,這個包需要調用操作系統的東西。

    • 安裝url-loader和file-loader用於對圖片進行處理,命令為:yarn add file-loader@1.1.6 url-loader@0.6.2 --dev

    • 安裝字體圖標庫,命令為:yarn add font-awesome

    • 提出處理公共模塊插件,需要定義在插件列表中,並且需要提前引入webpack

    const webpack = require('webpack');
    plugins: [
      new webpack.optimize.CommonsChunkPlugin({
            name: "common",
            filename: 'js/base.js'
      })
    ]
    
    • 正常情況下的打包文件命令是:

      • node_modules/.bin/webpack(僅在項目中安裝webpack時)
      • webpack(全局安裝webpack時)
    • 為了方便打包和查看文件,需要引入webpack-dev-server,做到文件改動服務實時更新的效果

      • 命令: yarn add webpack-dev-server@2.9.7 --dev
      • 配置:
        • 在output中配置:publicPath: '/dist/'
        • 在plugins后添加配置信息
      • 安裝成功后的打包命令為:node_modules/.bin/webpack-dev-server,啟動服務
    • 啟動服務后,如果在項目的調試中報錯:Refused to apply style from 'http://localhost:8080/dist/css/style.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled. ,解決辦法是找到.html文件去掉rel="stylesheet"刷新即可

  • 項目的最終打包:

    • 需要在package.json配置文件中配置
    "scripts":{
        "dev": "node_modules/.bin/webpack-dev-server", 
        "dist": "node_modules/.bin/webpack -p"
      },
    
    • dev:是開發環境的打包, 運行命令為: yarn run dev
    • dist: 是線上環境的打包,運行命令為:yarn run dist

二、React框架

(一)、基本的特點

  • 視圖層框架
    • 一個構建用戶界面的框架
    • 聲明式框架
    • 數據驅動DOM,再用事件反饋給數據,即數據顯示到DOM上和事件反饋給數據是兩個獨立的換件,在vue和angular上這兩個步驟是合並在一起的
  • 采用組件化方式
    • 組件結合,而不是繼承
    • state&&props
  • jsx表達式
    • 一種js擴展的表達式
    • 帶有邏輯的標記語法,有別於html模版
    • 對樣式、邏輯表達式和事件的支持
  • 虛擬DOM機制,只有在必要的時候才會操作dom,避免低效的操作
    • 對DOM進行模擬
    • 比較操作前后的數據差異
    • 如果有數據差異,統一操作DOM

(二)、基礎語法

  • ReactDom.render()用於渲染Dom元素,參數有兩個

    • 需要渲染的元素標簽
    • 通過document找到對應的Dom節點
  • jsx文件中標簽用className代替普通html中的class

  • 在render第一個參數中可以進行包括變量使用、條件判斷以及數組循環等多種操作

import React from 'react';
import ReactDom from 'react-dom';

let style = {
    color: 'purple',
    fontSize: '30px'
}

let name = 'liquanhui'
let names = ['lili', 'yangliu', 'xuhuan']
let flag = false

// 數據的邏輯處理
let jsx = (
    <div style={style}>
        {/* 變量的使用 */}
        <p> I am {name}</p>
        {/* 通過條件判斷控制變量的使用 */}
        {
            flag ? <p>I am {name}</p> : <p>I am not {name}</p>
        }
        {/* 數組循環 */}
        {
            names.map((name, index) => <p key={index}>Hello, I am {name}</p>)
        }
    </div>
);

ReactDom.render(
    jsx,
    document.getElementById('app')
);

(三)、React組件

  • 組件的定義方式

    • 方式一:

      • 先定義組件函數:function functionName(params){.....}
      • 把組件的名字以閉合標簽的形式添加到ReactDom.render的第一個參數中
    • 方式二(推薦使用):

      • es6語法下,定義組件需要用到類,病繼承React.Component,class className extends React.Component{render(){return ....}}
      • 把組件的名字以閉合標簽的形式添加到ReactDom.render的第一個參數中
    • 需要注意的是:當自定義多個組件的時候,組件不能直接全部添加到ReactDom.render的第一個參數中,需要先添加一個div標簽,把所有的組件閉合標簽添加到div標簽中,div標簽作為整體放在第一個參數中

import React from 'react';
import ReactDom from 'react-dom';

function Component() {
    return <h1>col</h1>
}

// Es6語法
class Es6Component extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: 'Rosen',
            age: 20
        }
        this.handleClick = this.handleClick.bind(this);
    }

    // handleClick事件處理
    handleClick() {
        this.setState({
            age: this.state.age + 1
        })
    }

    render() {
        setTimeout(() => {
            this.setState({
                name: 'Rosen Test'
            })
        }, 2000)
        return (
            <div>
                <h1>I am {this.state.name}</h1>
                <p>I am {this.state.age} years old</p>
                <button onClick={this.handleClick}>加一歲</button>
            </div>
        )
    }
}

ReactDom.render(
    <div>
        <Component />
        <Es6Component />
    </div>,
    document.getElementById('app')
);
  • React組件的生命周期
    • Mounting:掛載階段
    • Updating:運行時階段
    • UnMounting:卸載階段
    • Error Handling:錯誤處理,這里只處理render渲染時出現的錯誤,代碼邏輯中的錯誤處理不了
import React from 'react';
import ReactDom from 'react-dom';

class Component extends React.Component {
    // 構造方法
    constructor(props) {
        super(props);
        this.state = {
            data: 'old state'
        }
        console.log('初始化數據', 'constructor');
    }
    // 即將掛載
    componentWillMount() {
        console.log('componentWillMount');
    }
    // 掛載完成,組件渲染完成
    componentDidMount() {
        console.log('componentDidMount');
    }
    // 將要接收父組件傳來的props
    componentWillReceiveProps() {
        console.log('componentWillReceiveProps');
    }
    // 子組件是否應該更新,必須有boolen類型的返回值,默認為true
    shouldComponentUpdate() {
        console.log('shouldComponentUpdate');
        return true;
    }
    // 組件將要更新
    componentWillUpdate() {
        console.log('componentWillUpdate');
    }
    // 組件更新完成
    componentDidUpdate() {
        console.log('conponentDidUpdate');
    }
    // 即將銷毀組件
    componentWillUnmount() {
        console.log('componentWillUnmount');
    }

    // 處理點擊事件
    handleClick() {
        console.log('更新數據');
        this.setState({
            data: 'new state',
            hasChild: true
        });
    }
    // 渲染
    render() {
        console.log('render');
        return (
            <div>
                <div>Props: {this.props.data}</div>
                <button onClick={() => { this.handleClick() }}>更新組件狀態</button>
            </div>
        )
    }

}

class App extends React.Component{
    // 構造方法
    constructor(props) {
        super();
        this.state = {
            data: 'old props',
            hasChild: true
        }
        console.log('初始化父組件數據', 'constructor');
    }
    // 改變props
    onPropsChange() {
        console.log('更新Props');
        this.setState({
            data: 'new props'
        });
    }
    // 銷毀子組件
    destroyClick() {
        console.log('銷毀子組件');
        this.setState({
            hasChild: false
        })
    }
    render() {
        return (
            <div>
                {
                    this.state.hasChild ? <Component data={this.state.data} /> : null
                }
                <button onClick={() => { this.onPropsChange() }}>改變props</button>
                <br/>
                <button onClick={() => { this.destroyClick()}}>銷毀子組件</button>
            </div>
        )
    }
}

ReactDom.render(
    <div>
        <App />
    </div>,
    document.getElementById('app')
)

/**
* 生命周期順序
*/
初始化父組件數據 constructor
初始化數據 constructor
componentWillMount
render
componentDidMount
更新數據
shouldComponentUpdate
componentWillUpdate
render
conponentDidUpdate
更新Props
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
conponentDidUpdate
銷毀子組件
componentWillUnmount

(四)、Router原理及React-router

  • 常見的Router

    • 頁面Router:整個頁面重新渲染加載
    • Hash Router:只有頁面的hash值改變,整個頁面並不會重新加載
    • H5 Router:保證在完成路由的同時,不進行頁面的跳轉。既能操作hash,也可以操作路徑,但由於h5的原因,兼容性要差一些
  • 有了react-router之后,就可以開發spa應用了。spa(single page application):單頁應用。就是所有的功能都在一個頁面上完成,和傳統pc端網頁的都是a連接跳轉方式不同,主要類似於native app(原生app:一般底部都有導航欄,在做切換的時候頁面上的很多內容會被復用)的體驗。

  • 安裝命令:yarn add react-router-dom@4.2.2

import React from 'react';
import ReactDom from 'react-dom';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';

class A extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                Component A
                <Switch>
                    <Route exact path={`${this.props.match.path}`} render={(route) => {
                        return <div>當前組件是不帶參數的A</div>
                    }} />
                    {/**
                     * 為了保證路由可以正常的訪問,在路由的設置中必須把子路由放在通配參數路由之前,否則子路由無法訪問
                     */}
                    <Route path={`${this.props.match.path}/sub`} render={(route) => {
                        return <div>子路由信息</div>
                    }} />
                    <Route path={`${this.props.match.path}/:id`} render={(route) => {
                        return <div>當前組件是帶參數的A,參數是:{route.match.params.id}</div>
                    }} />
                </Switch>
            </div>
        )
    }
}

class B extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                Component B
            </div>
        )
    }
}

class Wrapper extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <Link to="/a">組件A</Link>
                <br />
                {/*帶參數的路由*/}
                <Link to="/a/123">帶參數的組件A</Link>
                <br />
                {/*子路由*/}
                <Link to="/a/sub">子路由</Link>
                <br/>
                <Link to="/b">組件B</Link>
                {this.props.children}
            </div>
        )
    }
}

ReactDom.render(
    <Router>
        <Wrapper>
            <Route path="/a" component={A} />
            <Route path="/b" component={B} />
        </Wrapper>
    </Router>,
    document.getElementById('app')
)

(五)、React數據管理

  • 數據通信的方式

    • 方式一:狀態提升方式。找到不同組件之間的共同祖先組件,當一個組件對共同祖先組件修改之后,其他的子組件對應的數據也會發生相應的改變
    graph LR 組件A --通過組件B對祖先組件數據進行修改--> 組件B 組件B-->共同祖先組件 共同祖先組件--組件A對祖先組件修改后,其他組件數據發生改變-->組件C 組件C-->組件D
    • 方式二:發布訂閱方式。不同組件之間存在一個共同委會的訂閱中心,每一個組件把自己的數據發布到訂閱中心,其他的組件按照對應的數據結構接收數據,這種方式不再像狀態提升方式一樣需要尋找共同的祖先組件。
    graph TB 組件A--傳入-->訂閱中心[(訂閱中心)] 組件B--傳入-->訂閱中心[(訂閱中心)] 組件C--傳入-->訂閱中心[(訂閱中心)] 組件D--傳入-->訂閱中心[(訂閱中心)] 訂閱中心[(訂閱中心)]--接收-->組件A 訂閱中心[(訂閱中心)]--接收-->組件B 訂閱中心[(訂閱中心)]--接收-->組件C 訂閱中心[(訂閱中心)]--接收-->組件D
    • 方式三:Redux單向數據流的方式,這種方式更類似於狀態提升和訂閱消息兩種方式的結合,不同於狀態提升的是,不同組件之間通信的中間組件不是共同祖先組件,而是所有組件的根組件(Store),組件發出action,action定義要做什么,Reducer接收,根據原有的action和state生成新的state,對根組件進行數據操作,根組件發生變化后,所有的組件數據也發生相應的改變。每一個組件都可以是改變方,同樣的每一個組件都可以是改變后的數據的接收方。
    graph TB 組件A--action-->Reducer Reducer--state-->store[(根組件Store)] store[(根組件Store)]--數據改變-->組件C store[(根組件Store)]--數據改變-->組件D store[(根組件Store)]--數據改變-->組件E store[(根組件Store)]--數據改變-->組件F store[(根組件Store)]--數據改變-->組件G
  • 應用場景:

    • 狀態提升方式:組件層級扁平,兄弟組件通信情況很少的業務場景
    • 發布訂閱:業務規模叫小,層級較深的業務場景
    • Redux:業務復雜,組件層級較深,兄弟組件通信密切等業務場景都可以很好的解決,但由於復雜度比較高,所以在狀態提升和發布訂閱方式不能很好解決的必要情狀下,再使用Redux單向數據方式

三、管理系統開發

(一)、通用部分開發

  • 引用Element UI用於構建頁面
    • 命令:npm i element-react --save npm install element-theme-default --save
    • 引入后,如果直接啟動,會報兩種錯誤:
      • Module not found: Error: Can't resolve 'babel-runtime/helpers/typeof'
      • Module not found: Error: Can't resolve 'babel-runtime/helpers/possibleConstructorReturn'
    • 解決辦法:
      • yarn add babel-runtime
      • npm i @babel/runtime --save-dev
      • npm i -D react-hot-loader@next


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM