react 项目打包优化 ===

目录

项目打包

项目本地预览

打包体积分析

生产环境优化

路由懒加载

去掉console

配置CDN


项目打包

通过命令对项目进行打包

  1. 在项目根目录打开终端,输入打包命令:npm run build

  2. 等待打包完成,打包后的内容被放在项目根下的 build 文件夹中

项目本地预览

 在本地预览打包后的项目

  1. 全局安装本地服务包:npm i -g serve,该包提供了 serve 命令,用来启动本地服务

  2. 在项目根目录中执行命令:serve -s ./build,在 build 目录中开启服务器

  3. 在浏览器中访问:http://localhost:5000/ 预览项目

打包体积分析

分析项目打包体积

  1. 安装分析打包体积的包:npm i source-map-explorer

  2. 在 package.json 中的 scripts 标签中,添加分析打包体积的命令

"scripts": {
  "analyze": "source-map-explorer 'build/static/js/*.js'",
}

 

  1. 对项目打包:npm run build(如果已经打过包,可省略这一步)

  2. 运行分析命令:npm run analyze

  3. 通过浏览器打开的页面,分析图表中的包体积

生产环境优化

 根据是否为生产环境对redux中间件进行优化

store/index.js 中 

let middlewares

if (process.env.NODE_ENV === 'production') {
  // 生产环境,只启用 thunk 中间件
  middlewares = applyMiddleware(thunk)
} else {
  middlewares = composeWithDevTools(applyMiddleware(thunk))
}

 

路由懒加载

对路由进行懒加载实现代码分隔

  1. 在 App 组件中,导入 Suspense 组件

  2. 在 Router 内部,使用 Suspense 组件包裹组件内容

  3. 为 Suspense 组件提供 fallback 属性,指定 loading 占位内容

  4. 导入 lazy 函数,并修改为懒加载方式导入路由组件

App.js 中

import { lazy, Suspense } from 'react'

// 导入页面组件
const Login = lazy(() => import('./pages/Login'))
const Layout = lazy(() => import('./pages/Layout'))

const App = () => {
  return (
    <Router history={history}>
      <Suspense
        fallback={
          <div
            style={{
              textAlign: 'center',
              marginTop: 200
            }}
          >
            loading...
          </div>
        }
      >
        <div className="app">
          <Route exact path="/" render={() => <Redirect to="/home" />}></Route>
          <Route path="/login" component={Login}></Route>
          <AuthRoute path="/home" component={Layout}></AuthRoute>
        </div>
      </Suspense>
    </Router>
  )
}

export default App

 pages/Layout/index.js 中

// 使用 lazy 导入组件
const Home = lazy(() => import('../Home'))
const Article = lazy(() => import('../Article'))
const Publish = lazy(() => import('../Publish'))

// ...

去掉console

npm i terser-webpack-plugin@4.2.3

const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  webpack: {
    // 省略其他...
		plugins: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: process.env.NODE_ENV === 'production' 
            // 生产环境下移除控制台所有的内容
          }
        }
      })
    ]
  }
}

配置CDN

对第三方包使用CDN优化

  1. 打包的时候不打包进来

  2. 在public/index.html中引入外部链接

分析说明:通过 craco 来修改 webpack 配置,从而实现 CDN 优化  

craco.config.js 中:

const path = require('path')
// HtmlWebpackPlugin
const { whenProd, getPlugin, pluginByName } = require('@craco/craco')
module.exports = {
  webpack: {
    alias: {
      '@': path.join(__dirname, 'src')
    },
    configure: (webpackConfig) => {
      let cdn = {
        js: [],
        css: []
      }
      // 对webpack进行配置
      whenProd(() => {
        // 只会在生产环境执行
        webpackConfig.externals = {
          react: 'React',
          'react-dom': 'ReactDOM',
          redux: 'Redux',
        }

        cdn = {
          js: [
            'https://cdn.bootcdn.net/ajax/libs/react/17.0.2/umd/react.production.min.js',
            'https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js',
            'https://cdn.bootcdn.net/ajax/libs/redux/4.1.0/redux.min.js'
          ],
          css: []
        }
      })

      const { isFound, match } = getPlugin(
        webpackConfig,
        pluginByName('HtmlWebpackPlugin')
      )
      if (isFound) {
        // 找到了html的插件
        match.options.cdn = cdn
      }

      return webpackConfig
    }
  }
}

 public/index.html 中:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
    <% htmlWebpackPlugin.options.cdn.css.forEach(cdnURL => { %>
      <link rel="stylesheet" href="<%= cdnURL %>"></link>
    <% }) %>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
    <% htmlWebpackPlugin.options.cdn.js.forEach(cdnURL => { %>
      <script src="<%= cdnURL %>"></script>
    <% }) %>
  </body>
</html>

上一篇:lazyLoad


下一篇:Just a Hook