从零开始搭建antd4.x + react16 + redux4 + webpack4 + react-router5基础框架解析

 

 

以上是2020年10月份的版本,后来,我将xmind进行了完善,文档也写的差不多了,可是,电脑坏了,硬盘换了,文件都没有了。这已经是第三次写这个文档了,思维导图就不更新了,按照几个重点进行说明。

这个框架,使用了几个月,也丰富了很多内容。回头看,重点是:

  1. 搭建框架顺序,先干什么后干什么,这个弄反了,各种报错都来了
  2. 依赖的平衡,不可能所有都用最新的依赖,因为依赖之间版本有关联,需要不断调整从中间找平衡。
  3. webpack配置,根据文档,按需配置
  4. 业务层面,layout,service拦截器,路由,store,换肤,组件复用等

一、webpack配置重点

Entry

入口起点(entry point)指示webpack应该使用哪个模块,来作为构建其内部依赖图的开始。
进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。
每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。

Output

output属性告诉webpack在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。
基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。

Module

模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。

Chunk

代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。

Loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript),模块转换器,用于把模块原内容按照需求转换成新内容。
loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

Plugin

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。

插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

webpack启动流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 :

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。
  3. 确定入口:根据配置中的 entry 找出所有的入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 。
  5. 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  6. 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
  7. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
  8. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

 

 

二、配置webpack

1、安装create-react-app

装npm,用npm init可以初始化生成package.json
我这里用的creact-react-app生成基本模块
create-react-app 中文文档:https://www.html.cn/create-react-app/docs/getting-started/

2、配置webpack.config.js

const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const dev = require('./webpack.dev');
const prod = require('./webpack.prod');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isDev = process.env.NODE_ENV === 'development';

const base = {
    entry: path.resolve(__dirname, '../src/index.js'),      // 入口
    module: {                                               
        rules: [{                                           // 对.js、.jsx的处理
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: 'babel-loader'
        }, {                                                // 对.css的处理
            test: /\.css$/,
            use: [
                !isDev && MiniCssExtractPlugin.loader,      // 生产环境下样式抽离
                isDev && 'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 1                    // 引入的文件调用后面的loader处理
                    }
                },
                {                                           // 智能添加样式前缀
                    loader: "postcss-loader",
                    options:{
                        plugins:[require('autoprefixer')]
                    }
                },
            ].filter(Boolean)
        }, {
            test: /\.scss$/,                               // css预处理器scss的处理
            use: [
                !isDev && MiniCssExtractPlugin.loader,     // 生产环境下抽离样式
                isDev && 'style-loader',
                "css-loader",
                "sass-loader"
            ].filter(Boolean)
        }, {
            test: /\.less$/,                               // css预处理器less的处理
            use: "less-loader",
        }, {
            test: /\.stylus$/,                             // css预处理器stylus的处理                           
            use: "stylus-loader",
        }, {
            test: /\.(jpe?g|png|svg|gif)$/,                // 对图片的处理
            loader: "file-loader",
            options: {
                name: "image/[contentHash].[ext]"
            },
        }, {
            test: /\.(woff|ttf|eot|otf|ico)$/,             // 对字体图标的处理
            loader: "file-loader",
            options: {
                name: "image/[name].[ext]"
            },
        }]
    },
    output: {                                              // 出口
        filename: 'scripts/[name].bundle.js',
        path: path.resolve(__dirname, '../dist')
    },
    resolve: {                                             // 引入js、jsx文件时,无需添加后缀
        extensions: ['.js', '.jsx'],
    },
    plugins: [
        !isDev && new MiniCssExtractPlugin({               // css样式抽离
            filename: 'css/[name].[contentHash].css'
        }),
        new HtmlWebpackPlugin({                            // 配置入口html
            filename: 'index.html',
            template: path.resolve(__dirname, '../public/index.html'),
            hash: true,
            inject: true,
            favicon: path.resolve(__dirname, '../public/favicon.ico'), 
            minify: !isDev && {
                removeAttributeQuotes: true,               // 去掉属性双引号
                collapseWhitespace: true,                  // 将html文件折叠成一行
            }
        }),
        new webpack.HotModuleReplacementPlugin(),
    ].filter(Boolean),
    devServer: {                                           // 配置服务
        hot:true,                                          // 热更新
        port: 3000,                                        // 端口号
        compress: true,                                    // 提升页面返回速度
        open: true,                                        // 启动服务后自动启动浏览器
        contentBase: path.resolve(__dirname, '../dist'),   // webpack启动服务会在dist目录下
    }
}

module.exports = () => {                                    // 根据环境合并webpack
    if (isDev) {
        return merge(base, dev);
    } else {
        return merge(base, prod);
    }
} 

3.proxy代理

我使用了webpack-dev-server来启动服务,script/start.js,以下是介绍:

webpack-dev-server是webpack官方提供的一个小型Express服务器。使用它可以为webpack打包生成的资源文件提供web服务。
webpack-dev-server 主要提供两个功能:
1.为静态文件提供web服务
2.自动刷新和热替换(HMR)

自动刷新指当修改代码时webpack会进行自动编译,更新网页内容
热替换指运行时更新各种模块,即局部刷新
具体配置方法,见官方文档:https://www.webpackjs.com/configuration/dev-server/#devserver

4.路由配置

app.js里面进行配置和测试,这里要注意,用的是react-router-dom,而不是react-router
开始我用的react-router,调试了很久,才弄好。另外还需要注意hashrouter和browserrouter的区别
react-router-dom官方文档:https://reactrouter.com/web/guides/quick-start

5.layout和redux同步进行

国际化用antd的:https://ant.design/docs/react/i18n-cn
redux主要是configureStore的配置,以下代码里面分别说明

//app.js
import React, { Component } from 'react';
import { Switch, Route, HashRouter as Router,Redirect} from 'react-router-dom';
import { Provider } from 'react-redux'
import {PersistGate} from 'redux-persist/lib/integration/react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN';//直接用antd的国际化就可以了,后来同组的人改成之前的了
import Error from '@/components/Exception/error';  //错误页面,用react自带getDerivedStateFromError判断,如果错误就调404页面
import { addLocaleData, IntlProvider } from 'react-intl'; 
//import { fetchToken } from '@/service/xhr/fetch'
import configureStore from "./stores/configureStore"; //redux 配置
import postcssFlexbugsFixes from 'postcss-flexbugs-fixes';

import BasicLayout from '@/layouts/BasicLayout'
import Login from '@/pages/login';//login测试使用,正式开发可去除


const {store, persistor } = configureStore();

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
            isError: false
        };
    // 只在开发环境获取 token
    // if(process.env.NODE_ENV === "development"){
    //   fetchToken()
    // }
  }

  static getDerivedStateFromError(error) {
        return {
            isError: true
        }
  }
  
  render() {
   // const routers = getLayoutData();
    if(this.state.isError){
            return <Error />
        }
    return (
      <Provider store={store} >
        <PersistGate loading={null} persistor={persistor}>
        <Router>
          <ConfigProvider locale={zhCN}>
          <IntlProvider locale={zhCN} messages={zhCN}>
            <Switch>
            <Route  path='/portal/login' component={Login} />
              <Route  path='/portal' component={BasicLayout} />
              <Redirect to='/portal/login' />
            </Switch>
        </IntlProvider>
            
          </ConfigProvider>
        </Router>
        
        </PersistGate>
      </Provider>
    );
  }
}

export default App;
//layout.js本来想放上来的,发现由于UI设计,写了很多样式判断
//关键点就是router循环渲染出menu,没有什么特别高深的
//redux配置
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' //中间件
import createLogger from 'redux-logger' //开发环境,打印出dispatch action方便调试
import rootReducer from '@/models/rootReducer'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2'
export default function configureStore() {
const persistConfig = {
key: 'root',
storage,
stateReconciler: autoMergeLevel2,
// whitelist: ['fetchDatacenter','getConfig','fetchVirtualDatacenterList',]
whitelist: []
};
const persistedReducer = persistReducer(persistConfig, rootReducer)
let middleware = [thunk];
if(process.env.NODE_ENV !== 'production'){
middleware.push(createLogger)
}
const store = createStore(
persistedReducer,
undefined,
applyMiddleware(...middleware),
);
let persistor = persistStore(store)
return {
store,
persistor
}
}

6.公共方法

  • table公共组件,白同学写的,直接调用可以生成table
  • 换肤,将theme写在redux里面,根据获取的theme渲染不同的样式
  • login,权限,角色等常规业务

以上就是全部内容了,框架在我们公司git上,也可以按照我之前的文件脚手架下载,看代码了解。
【完】

参考

webpack配置:https://www.runoob.com/w3cnote/webpack-tutorial.html
antd4.x与3.x的不同 https://ant.design/docs/react/migration-v4-cn
redux的store配置官方文档:http://cn.redux.js.org/docs/recipes/ConfiguringYourStore.html
redux-logger:https://github.com/LogRocket/redux-logger
webpack优化:https://juejin.cn/post/6844904093463347208

posted @ 2021-04-16 18:00  优前程  阅读(525)  评论(0编辑  收藏  举报