最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基础-搭建react项目教程

    正文概述 掘金(那些年丶ny)   2020-12-27   367

    前言

    本文详细的介绍如何从头开始搭建一个react项目,帮助新人了解项目搭建流程和打包流程。
    需要注意包版本问题,不同版本代表着不同的代码。如果包版本不同启动项目时可能会出现未知错误。

    目录说明

    └─config                             #webpack配置文件
        │  webpack.build.config.js       #webpack生产配置文件
        │  webpack.dev.config.js         #webpack开发配置文件
    │  dist                              #打包后的文件
    │  node_modules                      #npm下载的包文件
    └─server                             #json-server 的数据来源
        │  db.json			             #数据
    └─src                                #项目源码
        │  images                        #图片文件      
        ├─pages                          #页面目录
            │  home.css                  #样式文件
            │  Home.js                   #页面组件
            │  home.less                     
            │  Page1.jsx                       
            │  Page2.tsx                 #ts 文件
            │  Thunk.js                      
        ├─store                          #redux store配置
            │  action.js                 
            │  actionsUser.js            
            │  reducer.js                     
        ├─types 
        	│  types.d.ts                #自定义 ts类型
        │ index.html
        │ index.js                       #入口文件
        │ router.js                      #路由文件
    │ .gitignore                         #git 上传过滤
    │ babel.config.json                  #babel 配置文件 配置插件使用 
    │ package-lock.json                  #包版本
    │ package.json                       #npm 配置文件
    │ postcss.config.js                  #postcss 配置文件
    │ README.md                          #简介
    │ tsconfig.json                      #ts配置检查文件
        
        
    

    node

    Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台 JavaScript 运行环境。
    安装node教程
    版本测试 --通过命令行测试版本号 基础-搭建react项目教程

    npm

    npm(全称 Node Package Manager,即“node包管理器”)是Node.js默认的、用JavaScript编写的软件包管理系统。NPM 使用介绍
    如网速不好时可以使用淘宝npm镜像,安装成功后只需要把npm修改为cnpm执行就可以了。

    npm install cnpm -g --registry=https://registry.npm.taobao.org
    

    创建项目

    创建项目根目录。可通过命令行也可自己创建文件夹。

    mkdir react-obj 
    

    初始化项目,后续命令都是进入根目录后执行。(可一直回车生)

    npm init // 按照提示填写项目基本信息 生成 package.json 文件
    

    webpack

    安装webpackwebpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。

    npm install --save-dev webpack
    npm install --save-dev webpack@<version> // 安装指定版本
    npm install --save-dev webpack-cli
    

    npm 命令简写
    install 安装命令 简写:i
    --save-dev 开发时要依赖的东西 简写:-D
    --save 发布时要依赖的东西 简写:-S
    -g 全局安装包(加载命令最后)
    注意:在webpack4之后 webpack-cli 和 webpack 就分为两个包

    基础的配置 webpack文档, 新建config目录放置webpack开发配置webpack.dev.config.js

    mkdir config 
    cd config 
    echo. > webpack.dev.config.js // window 创建文件
    
    // webpack.dev.config.js
    const path = require('path');
    
    module.exports = {
        /* 入口 */
        entry: path.join(__dirname, '../src/index.js'),
        /* 输出到dist目录,输出文件名字为bundle.js */
        output: {
            path: path.join(__dirname, '../dist'),
            filename: 'bundle.js'
        }
    };
    

    新建index.js入口文件

    mkdir src 
    cd src 
    echo. > index.js 
    
    // index.js
    document.getElementById('root').innerHTML = "Hello React";
    

    修改 package.json 文件中 "scripts" 的值

    {
      "name": "react-obj",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "build": "webpack --config ./config/webpack.dev.config.js"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^5.11.0",
        "webpack-cli": "^4.2.0"
      }
    }
    

    执行打包命令

    npm run build
    
    • 全局安装 webpack 和 webpack-cli
    • 也可以直接执行打包命令 webpack --config ./config/webpack.dev.config.js

    执行完成后可以看到生成了dist文件夹和bundle.js。内容就是index.js中的内容表示打包成功。

    Babel

    Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。Babel文档

    • @babel/core 调用Babel的API进行转码
    • @babel/preset-env 用于解析 ES6+
    • @babel/preset-react 用于解析 JSX
    • babel-loader 加载器
    npm i @babel/core @babel/preset-env @babel/preset-react babel-loader -D
    

    在根目录添加babel的配置文件 babel.config.json

    {
      "presets": ["@babel/preset-react", "@babel/preset-env"],
      "plugins": []
    }
    

    修改 webpack.dev.config.js 文件

    module.exports = {
      ...
      /* cacheDirectory是用来缓存编译结果,下次编译加速 */
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            use: ["babel-loader?cacheDirectory=true"],
            include: path.join(__dirname, "../src"),
          },
        ],
      }
      
    };
    

    修改 src/index.js

    var tem = str => {
        document.getElementById('root').innerHTML = str;
    };
    tem('我现在在使用ES6!');
    

    在dist文件夹下创建index.html

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    <div id="root"></div>
    <script type="text/javascript" src="./bundle.js"></script>
    </body>
    </html>
    

    执行打包命令

    npm run build
    

    用浏览器打开index.html文件看到 我现在在使用ES6! 打包成功。

    React

    npm i react react-dom -S
    

    修改 src/index.js

    import React from 'react';
    import ReactDom from 'react-dom';
    
    class Hello extends React.Component  {
        render() {
            return (
                <div>
                    开始使用React!
                </div>
            )
        }
    }
    
    ReactDom.render(<Hello />, document.getElementById('root'));
    

    执行打包命令

    npm run build
    

    打开inde.html文件 看到 开始使用! 打包成功.

    react-router

    在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI (--react组件) 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。
    react-router 也是使用javascript自带的 hashhistory api实现的。

    npm i react-router-dom -S
    

    现在开始使用路由。在src下创建文件夹pages和文件router.js,创建文件Home.jsPage1.jsPage2.js

    cd src
    mkdir pages 
    echo. > router.js
    cd pages
    echo. > Home.js
    echo. > Page1.js
    echo. > Page2.js
    
    // src/psges/Home.js
    import React from 'react';
    
    export default class Hello extends React.Component  {
        render() {
            return (
                <div>
                    我是首页
                </div>
            )
        }
    }
    
    // src/psges/Page1.js
    import React from "react";
    
    export default function () {
      return <div>我是页面1</div>;
    }
    
    // src/psges/Page2.js
    import React from "react";
    
    export default function () {
      return <div>我是页面2</div>;
    }
    
    // src/router.js
    import React from "react";
    // 引入路由组件
    import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
    import Home from "./pages/Home";
    import Page1 from "./pages/Page1";
    import Page2 from "./pages/Page2";
    
    // 添加404页面
    function NotFound(){
        return <div>404</div>;
    }
    
    export default function () {
      return (
        <BrowserRouter>
          {/* 开始使用路由 */}
          <div>
            <div>
              {/* 通过 Link 修改浏览器的url Switch组件 通过url 来判断展示那个Route 包裹的组件页面 */}
              <Link to="/">首页</Link>
            </div>
            <div>
              <Link to="/page1">Page1</Link><br></br>
              <Link to="/page2">page2</Link>
            </div>
            <Switch>
              <Route exact path="/" component={Home} />
              <Route path="/page1" component={Page1} />
              <Route path="/page2" component={Page2} />
              {/* 路由找不到 加载404组件 */}
              <Route component={NotFound} />
            </Switch>
          </div>
        </BrowserRouter>
      );
    }
    
    
    // 修改 src/index.js
    import React from 'react';
    import ReactDom from 'react-dom';
    import Root from './router';
    
    ReactDom.render(<Root />, document.getElementById('root'));
    

    现在执行打包命令,打开index.html看到页面能正常输出。但是路由是不能跳转的,因为现在是根目录打开无法跳转路由。
    需要安装 webpack-dev-server,是webpack官方提供的一个小型Express服务器。

    npm i webpack-dev-server -D
    
    // 修改 package.json
    "scripts": {
      "start": "webpack-dev-server --config ./config/webpack.dev.config.js",
      "build": "webpack --config ./config/webpack.dev.config.js"
    },
    
    //在 `webpack.dev.config.js` 添加配置
    module.exports = {
      ...
      
      devServer: {
        contentBase: path.join(__dirname, '../dist'), 
        compress: true,  // gzip压缩
        host: '0.0.0.0', // 允许ip访问
        hot:true, // 热更新
        historyApiFallback:true, // 解决启动后刷新404
        port: 8000 // 端口
      },
    
    };
    

    --这里就遇到了包版本问题 前面安装webpack-cli@4 版本 和 webpack-dev-server@3 版本 是不兼容的 启动就会报错。这里把webpack-cli降低了版本。

    npm uninstall webpack-cli -D
    npm i webpack-cli@3 -D
    

    启动服务,打开http://localhost:8000 就能开始使用路由了

    npm start
    

    devtool

    在开发时需要定位错误,打包后的代码比较难以理解,这时候只需要在webpack.dev.config.js添加devtool 就能在错误的时候定位到自己写的代码。

    devtool: 'inline-source-map',
    

    React-Redux

    Redux 其实就是找个,不能直接操作的对象存放一些共享数据。只能通过特殊的方式来获取和修改数据。我们这里使用的React-Redux其实就是对Redux进行的一次封装,把那个对象放在了props上。

    npm i redux react-redux -S
    

    下面我们来吧Home.js修改为使用react-redux的计数器。

    // Home.js
    import React from "react";
    // 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
    import { connect } from 'react-redux';
    // 定义 store的修改类型
    import { addAction, reduceAction } from "../store/action";
    
    function Home(props) {
      const { count, onAddClick, onReduceClick } = props;
      return (
        <div>
          <span>我是首页计数器{count}</span>
          <button onClick={onAddClick}>加</button>
          <button onClick={onReduceClick}>减</button>
        </div>
      );
    }
    // 获取store中的值到当前组件
    function mapStateToProps(state) {
      return {
        count: state.count,
      };
    }
    // 修改store中的值
    function mapDispatchToProps(dispatch) {
      // 获取action.js 中定义的特殊类型
      return {
        onAddClick: () => dispatch(addAction),
        onReduceClick: () => dispatch(reduceAction),
      };
    }
    // connect接收两个参数 叫什么都行
    // 第一个参数获取 reducer.js 中 counter 定义的state的值
    // 第二个参数通过 回调 dispatch 传入指定类型 来修改 reducer.js 中 counter 定义的state
    export default connect(mapStateToProps, mapDispatchToProps)(Home);
    

    在根目录创建store文件夹,在其中创建action.jsreducer.js

    cd src
    mkdir store 
    cd store
    echo. > action.js
    echo. > reducer.js
    
    // store/reducer.js 创建redux中的store对象,设置修改对象的类型。
    import { createStore } from 'redux';
    
    function counter(state = { count: 0 }, action) {
      const count = state.count;
      switch (action.type) {
        case "add":// 约定的类型
          return { count: count + 1 };
        case "reduce":
          return { count: count - 1 };
        default:
          return state;
      }
    }
    
    // 创建 Store
    const store = createStore(counter)
    // 导出要使用的Store
    export default store
    
    // store/action.js 
    // 根据reducer.js 设置的约定类型 定义修改store的 公用action对象
    const addAction = { type: 'add' }
    const reduceAction = { type: 'reduce' }
    
    export { addAction, reduceAction}
    
    // 修改router.js 
    // 配置store对象 放入组件最顶级的props中
    ...
    import { Provider } from 'react-redux'
    import Home from "./pages/Home";
    import Page1 from "./pages/Page1";
    import Page2 from "./pages/Page2";
    import store from "./store/reducer";// 引入store
    
    ...
    
    export default function () {
      return (
        <Provider store={store}>
          {/* 把store放入props中让所有子组件都能获取到  */}
          ...
        </Provider>
      );
    }
    
    // 修改pages/Page1.js 测试 --当在其他组件中修改store的值后 每个组件对应的值都修改了
    import React from "react";
    import { connect } from 'react-redux'
    
    function Home(props) {
      const { count } = props;
      return <div>我是页面1--{count}</div>;
    }
    // 获取store中的值
    export default connect((state)=>({count: state.count}))(Home);
    

    启动项目,当Home.js中修改计数值,切换路由到Page1.js计数值跟着修改。这样Redux就创建成功了。
    在这里梳理一下我们做了什么。
    1.通过 reducer 创建我们需要的store对象和修改store对象的类型,这里可以创建多个reducer合并在一起。
    2.通过 action 设置公用的约定类型对象。
    3.通过 Provider 把store 放入顶部props。
    4.通过 connect 第一个参数获取store的值,通过第二个参数传入action对象修改store。

    集成TypeScript

    npm i typescript ts-loader -D
    

    typescript 基础安装包
    ts-loader 解析.ts文件 为 es5的.js文件

    修改package.jsonscripts,然后 npm run tsc 初始化 TypeScript ,生成 tsconfig.json 文件,管理工程配置,例如你想包含哪些文件和进行哪些检查。

      "scripts": {
        ...
        "tsc": "tsc --init"
      },
    

    webpack.dev.config.js的 module -> rules 中添加

    module: {
        rules: [
          ...
          {
            test:/\.(ts|tsx)?$/,
            use:'ts-loader',
            include: path.join(__dirname, "../src"),
            exclude: /node_modules/
          },
        ],
    },
    
    // webpack.dev.config.js 中添加
    // import导入文件 无需后缀配置
    module.exports = {
      ...
      resolve: {
        extensions: [".js", ".jsx", ".json",".ts",".tsx"]
      },
    }
    

    修改 Page2.jsPage2.tsx,发现文件报错。
    安装 react@types/react 获取ts类型,修改tsconfig.json 添加 "jsx": "react" 设置jsx验证。
    当遇见框架没有@types包时在src下创建types -> types.d.ts在文件中自定义类型。

    npm i @types/react -D
    
    // tsconfig.json
    {
      "compilerOptions": {
        "jsx": "react",
        ...
      },
      "include": [  // 所需要用ts的文件
        "src/**/*",
      ],
    }
    
    cd src
    mkdir types 
    cd types
    echo. > types.d.ts
    
    // types.d.ts
    declare module "react-redux" {
        export {}
    }
    
    // Page2.tsx
    import React from "react";
    
    // 使用ts设置 强类型
    interface pageProps{
      num?:number,
      text:string,
    }
    
    function Test(props:pageProps){
      return <div>{props.text}{props.num}</div>;
    }
    
    export default function () {
    
      return <Test text="使用Ts。你好我是页面" num={2}/>;
    }
    

    重新启动页面正常加载,配置成功,就可以正常使用ts了。

    添加css

    npm i css-loader style-loader -D
    

    css-loader 解析类似@import 和 url(...)等方法实现。
    style-loader 解析css文件,打包后加入页面。

    webpack.dev.config.js的 module -> rules 中添加

    {
      test: /\.css$/,
      use: ["style-loader", "css-loader"],
    },
    

    在pages中创建 home.css

    echo. > home.css
    
    // home.css
    .home {
        border: 1px solid rgb(108, 92, 153);
        display: flex;
    }
    
    // 修改`Home.js`
    ...
    import "./home.css"
    
    function Home(props) {
      ...
      return (
        <div className="home">
          ...
        </div>
      );
    }
    ...
    

    集成PostCSS优化

    集成PostCSS 后,可以自动给css属性加浏览器前缀。
    postcss-cssnext 允许你使用未来的 CSS 特性(包括 autoprefixer)。

    npm i postcss-loader postcss-cssnext -D
    

    修改webpack.dev.config.jsmodule -> rules

    {
      test: /\.css$/,
      use: ["style-loader", "css-loader","postcss-loader"],
    },
    

    然后在根目录下新建postcss.config.js

    module.exports = {
        plugins: {
            'postcss-cssnext': {}
        }
    };
    

    启动服务npm start查看css是否加上前缀,加上表示成功。

    添加less

    npm i less less-loader -D
    

    less 基础安装包
    less-loade 把.less解析为.css文件 在webpack.dev.config.js的 module -> rules 中添加

    {
      test: /\.less$/,
      use: ["style-loader","css-loader",
        {
          loader: "less-loader",
          options: {
            lessOptions: {
              strictMath: true,
            },
          },
        },
        "postcss-loader",
      ],
    },
    

    在pages中创建 home.less

    echo. > home.less
    
    .home {
        .hLess{
            color: #379adb;
        }
    }
    
    // 修改`Home.js`
    ...
    import "./home.less";
    
    function Home(props) {
      ...
      return (
        <div className="home">
         <span className="hLess">--我是首页计数器{count}</span>
          ...
        </div>
      );
    }
    ...
    

    启动服务npm start看见 我是首页计数器 改变颜色表示加载成功。

    添加图片

    npm i url-loader file-loader -D
    

    在src中添加images文件夹放入图片,修改Home.js

    cd src
    mkdir images
    
    // Home.js
    ...
    import timg from "../images/timg.jpg"
    
    function Home(props) {
      ...
      return (
        <div className="home">
          ...
          <img src={timg} />
        </div>
      );
    }
    ...
    

    webpack.dev.config.js module -> rules 中添加

    {
      test: /\.(png|jpg|gif)$/,
      use: [
        {
          loader: "url-loader",
          options: {
            // 控制小于10K的图片会被转成base64编码,直接插入HTML中.
            limit: 10240,
          },
        },
      ],
    },
    

    添加Ajax请求

    现在没有后端服务,我们使用 json-server 来模拟后端服务,然后使用axios发起ajax请求。

    npm i json-server -D
    npm i axios -S
    

    创建server文件夹,在里面创建db.json

    mkdir server
    cd server
    echo. > db.json
    
    {
      "users": [
        {
          "name": "a",
          "id": 1
        },
        {
            "name": "b",
            "id": 2
          }
      ]
    }
    

    修改package.jsonscripts

    "server":"json-server server/db.json --watch --port 3000"
    

    使用时需要分别启动两个服务,启动json-server后可直接查看数据。
    因为启用了两个服务发送请求时需要跨域,需要设置代理proxy修改webpack.dev.config.js

    devServer: {
        ...
        proxy: { // 配置服务代理
          '/api': {
               target: 'http://localhost:3000',
               // 转换请求 /api/users 为 http://localhost:3000/users
               pathRewrite: {'^/api' : ''},  
               changeOrigin: true
          }
        },
      },
    

    修改Home.js

    import React,{useEffect,useState} from "react";
    // 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
    import { connect } from "react-redux";
    import axios from "axios";
    // 定义 store的修改类型
    import { addAction, reduceAction } from "../store/action";
    import "./home.css";
    import timg from "../images/timg.jpg";
    
    function Home(props) {
      const { count, onAddClick, onReduceClick } = props;
    
      // 使用HOOK state
      const [state,setState] = useState([]);
    
      // 使用HOOK 副作用函数 --第二个参数为空表示 子执行一次 
      // 有值 表示对应的值修改 就在执行一次
      useEffect(()=>{
        // 发送请求
        axios.get("/api/users").then((res) => {
          let data = JSON.parse(res.request.responseText);
          setState(data)
        });
      },[])
    
      return (
        <>
          <div className="home">
            <span className="hLess">{state[0]?.name}--我是首页计数器{count}</span>
            <button onClick={onAddClick}>加</button>
            <button onClick={onReduceClick}>减</button>
          </div>
          <img src={timg}  />
        </>
      );
    }
    // 获取store中的值到当前组件
    function mapStateToProps(state) {
      return {
        count: state.count,
      };
    }
    // 修改store中的值
    function mapDispatchToProps(dispatch) {
      // 获取action.js 中定义的特殊类型
      return {
        onAddClick: () => dispatch(addAction),
        onReduceClick: () => dispatch(reduceAction),
      };
    }
    // connect接收两个参数 叫什么都行
    // 第一个参数获取 reducer.js 中 counter 定义的state的值
    // 第二个参数通过 回调 dispatch 传入指定类型 来修改 reducer.js 中 counter 定义的state
    export default connect(mapStateToProps, mapDispatchToProps)(Home);
    

    分别启动两个服务

    npm start
    npm run server
    

    redux-thunk中间件使用

    在使用redux中需要通过请求获取数据修改store对象。但是redux的action必须是个对象不能直接传入函数。所以需要使用中间件来实现异步修改数据。

    npm i redux-thunk -S
    

    先创建user数据的reducer、action 和 测试页面。

    // store/actionsUser.js
    
    import axios from 'axios';
    
    export const INFO = "user/INFO";
    // 异步修改 store 的 state 
    // connect函数 中的第二个参数需要的值
    export function getUser() {
        return dispatch => {
            axios.get('/api/users').then((res)=>{
                let data = JSON.parse(res.request.responseText);
                dispatch({
                    type: INFO,
                    payload: data
                });
            })
        }
    }
    
    // 修改 store/reducer.js
    
    import { createStore,applyMiddleware } from 'redux';
    import thunkMiddleware from 'redux-thunk';
    import { INFO } from "./actionsUser";// 获取约定类型
    
    // 计数器
    function counter(state = { count: 0 }, action) {
      switch (action?.type) {
        case "add":
          return { ...state,count: state.count + 1 };
        case "reduce":
          return { ...state,count: state.count - 1 };
        default:
          return state;
      }
    }
    
    // 异步修改
    function reducer(state = { user:{} }, action) {
      switch (action?.type) {
          case INFO:
              return {
                  ...state,
                  user: action.payload,
              };
          default:
              return state;
      }
    }
    
    // 合并 reducer
    function combineReducers(state = {}, action) {
      return {
          counter: counter(state.counter, action),
          reducer: reducer(state.user, action)
      }
    }
    
    // 创建store 对象
    const store = createStore(combineReducers,applyMiddleware(thunkMiddleware));
    
    // 导出要使用的Store
    export default store
    

    创建 pages/Thunk.js

    import React from "react";
    // 引入 react-redux 中的高阶函数 绑定获取和修改的特殊方法
    import { connect } from "react-redux";
    // 定义 store的修改类型
    import { getUser } from "../store/actionsUser";
    
    function Home(props) {
      const { user = [], getUser} = props;
    
      return (
        <div>
          <span>我是异步修改数据{user[0]?.name}</span>
          <button onClick={getUser}>修改</button>
        </div>
      );
    }
    // 获取store中的值到当前组件
    function mapStateToProps(state) {
      return {
        user: state.reducer.user,
      };
    }
    
    export default connect(mapStateToProps, {getUser})(Home);
    

    因为reducer.js修改,修改Home.jsPage1.js获取正常数据。

    ...
    // 获取store中的值到当前组件
    function mapStateToProps(state) {
      return {
        count: state.counter.count,
      };
    }
    ...
    
    ...
    // 获取store中的值
    export default connect((state)=>({count: state.counter.count,}))(Home);
    

    最后在router.js中添加组件路由。

    ...
    import Thunk from "./pages/Thunk.js";
    ...
    
    return (
      ...
      <div>
        <Link to="/thunk">thunk页</Link>
      </div>
      <Switch>
          ...
          <Route exact path="/thunk" component={Thunk} />
      </Switch>
      ...
    )
    

    npm start 重新启动,进入路由/thunk点击修改,能获取到数据,表示异步加载数据成功。

    缓存优化

    浏览器自带文件缓存,当第一次加载了bundle.js,第二次就不会下载这个文件,需要客户清空本地缓存后才会重新下载。
    所以我们生成文件时都生成不同的名字。修改webpack.dev.config.js 的 output。 filename 中自动生成文件的参数。 基础-搭建react项目教程

    module.exports = {
      ...
      output: {
          path: path.join(__dirname, "../dist"),
          filename: '[name].[chunkhash].js',
      }
      ...
    }
    

    提取公共包

    我们通过npm下载的公共库中的代码是不会修改的,所以不需要打包在bundle.js中,需要我们提取为公共代码。
    webpack4之前使用的CommonsChunkPlugin插件,在webpack4之后改为optimization.splitChunks。

    module.exports = {
      ...
      // 优化项配置
      optimization: { 
        // 分割代码块
        splitChunks: { 
          // -默认作用于异步chunk,值为all/initial/async/function(chunk),
          // -值为function时第一个参数为遍历所有入口chunk时的chunk模块,
          // -chunk._modules为chunk所有依赖的模块,通过chunk的名字和所有依赖模块的resource可以自由配置,
          // -会抽取所有满足条件chunk的公有模块,以及模块的所有依赖模块,包括css
          // chunks: "all"
          // 设置缓存组用来抽取满足不同规则的chunk
          cacheGroups: {
            // 抽取第三方模块
            vendor: { 
              name: 'vendor',// 模块名
              test: /node_modules/, // 如果你多次引用了node_modules第三方模块,就抽取出来
              chunks: "all"
            }
          }
        }
      },
      ...
    }
    

    HtmlWebpackPlugin优化

    npm i html-webpack-plugin -D
    

    每次自动把js插入到模板index.html里面。新建src/index.html,把dist文件夹删除。

    cd src
    echo. > index.html
    
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    <div id="root"></div>
    </body>
    </html>
    

    修改webpack.dev.config.js,增加plugin

    ...
    var HtmlWebpackPlugin = require("html-webpack-plugin");
    module.exports = {
    	...
        plugins: [
          new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.join(__dirname, "../src/index.html"),
          }),
        ],
      ...
    }
    

    重新运行npm start如果能正常访问就表示成功。

    文件压缩

    使用uglifyjs-webpack-plugin来压缩文件,使打包出来的文件体积更小。

    npm i uglifyjs-webpack-plugin -D
    
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
    
    module.exports = {
      plugins: [
        new UglifyJSPlugin()
      ]
    }
    

    webpack4+中添加了 模式(mode) 设置模式为production 会自动安装这个插件

    module.exports = {
      mode:"production"
    }
    

    清空上一次的打包文件

    webpack 每次打包,都会生成新的文件放入dist文件夹下,我们可以使用clean-webpack-plugin在打包前清除上一次的文件。

    npm i clean-webpack-plugin -D
    
    // 清空上一次的打包文件
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    ...
    plugins: [
        ...
        new CleanWebpackPlugin()
    ],
    ...
    

    生产坏境构建

    开发项目和打包发版时使用webpack的插件是不一样的。
    所以我们创建一个和 webpack.dev.config.js 内容一样的 webpack.build.config.js,根据不同环境修改配置。

    // package.json 修改 build 时调用 webpack.build.config.js
    "scripts": {
        "start": "webpack-dev-server --config ./config/webpack.dev.config.js",
        "build": "webpack --config ./config/webpack.build.config.js",
        "server": "json-server server/db.json --watch --port 3000",
        "tsc": "tsc --init"
    },
    
    // 删除开发时才需要的配置
    const path = require("path");
    // html自动打包
    var HtmlWebpackPlugin = require("html-webpack-plugin");
    // 清空上一次的打包文件
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    module.exports = {
      // 模式 开始模式 会自动预设安装插件 模式不同安装插件不同
      // 可以使用 node 自带的 process.env.NODE_ENV 来获取所在的环境
      mode: 'production',// production 生产模式  development 开发模式
    
      /* 入口 */
      entry:  path.join(__dirname, "../src/index.js"),
    
      /* 输出到dist目录,输出文件名字为bundle.js */
      output: {
        path: path.join(__dirname, "../dist"),
        filename: '[name].[chunkhash].js',
      },
      // 加载打包前的代码
      devtool: "cheap-module-source-map",
    
      /* cacheDirectory是用来缓存编译结果,下次编译加速 */
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            use: ["babel-loader?cacheDirectory=true"],
            include: path.join(__dirname, "../src"),
          },
          {
            test:/\.(ts|tsx)?$/,
            use:'ts-loader',
            include: path.join(__dirname, "../src"),
            exclude: /node_modules/
          },
          {
            test: /\.css$/,
            use: ["style-loader", "css-loader","postcss-loader"],
          },
          {
            test: /\.(png|jpg|gif)$/,
            use: [
              {
                loader: "url-loader",
                options: {
                  // 控制小于10K的图片会被转成base64编码,直接插入HTML中.
                  limit: 10240,
                },
              },
            ],
          },
          {
            test: /\.less$/,
            use: ["style-loader","css-loader",
              {
                loader: "less-loader",
                options: {
                  lessOptions: {
                    strictMath: true,
                  },
                },
              },
              "postcss-loader",
            ],
          },
        ],
      },
    
      plugins: [
        new HtmlWebpackPlugin({
          /* html压缩 */
          minify: true,
          template: path.join(__dirname, "../src/index.html"),
        }),
        new CleanWebpackPlugin()
      ],
      
      // 优化项配置
      optimization: { 
        // 分割代码块
        splitChunks: { 
          // -默认作用于异步chunk,值为all/initial/async/function(chunk),
          // -值为function时第一个参数为遍历所有入口chunk时的chunk模块,
          // -chunk._modules为chunk所有依赖的模块,通过chunk的名字和所有依赖模块的resource可以自由配置,
          // -会抽取所有满足条件chunk的公有模块,以及模块的所有依赖模块,包括css
          // chunks: "all"
          // 设置缓存组用来抽取满足不同规则的chunk
          cacheGroups: {
            // 抽取第三方模块
            vendor: { 
              name: 'vendor',// 模块名
              test: /node_modules/, // 如果你多次引用了node_modules第三方模块,就抽取出来
              chunks: "all"
            }
          }
        }
      },
      // 导入文件 无需后缀
      resolve: {
        extensions: [".js", ".jsx", ".json",".ts",".tsx"]
      },
    };
    
    

    参考资料

    菜鸟教程
    Babel教程
    webpackjs教程
    Web 前端路由原理解析和实现
    Redux 入门教程 TypeScript教程
    搭建React全家桶框架教程

    源码地址: github.com/nie-ny/reac…


    起源地下载网 » 基础-搭建react项目教程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元