0
点赞
收藏
分享

微信扫一扫

使用 vite 代替 webpack 搭建 react 前端开发环境

独西楼Q 2022-01-26 阅读 152

说明

  1. 快速启动本地开发服务器
  2. 代码更新快速显示在浏览器页面中
  • 在开发阶段 vite 会使用 esbuild ,同时 基于 原生 ES 模块实现 高效的 HMR
  • 在生产阶段 vite 会使用 rollup 来打包代码,输出优化过的静态资源

1. 简单搭建 vite 项目

通过脚手架搭建项目

npm init vite@latest # npm
yarn create vite # yarn
.
├── dist # build 后输出目录
├── index.html # 页面入口
├── package.json
├── src # 源文件
├── tsconfig.json
├── vite.config.ts # vite 配置文件
└── yarn.lock

浏览器中使用 es module

<body>
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>
</body>

内置命令

{
  "scripts": {
    "dev": "vite", // 启动开发服务器
    "build": "vite build", // 为生产环境构建产物
    "serve": "vite preview" // 本地预览生产构建产物 需要先 build 再 预览
  }
}

常用功能实现

1. 支持使用 JSX 语法及 HMR 热模块加载

const react = require('@vitejs/plugin-react');

return defineConfig({
  plugins: [react()]
});
const react = require('@vitejs/plugin-react');

return defineConfig({
  plugins: [react({
    babel: {
      presets: [...],
      plugins: [...],
      babelrc: true, // 是否直接使用外部的 .babelrc 配置文件
      configFile: true, // 是否直接使用外部的 babel.config.js 配置文件
    }
  })]
});

2. Typescript 支持

  • TS 编译器选项 compilerOptions.isolatedModules = true
  • TS 编译器选项 compilerOptions.useDefineForClassFields = true
  • TS 编译器选项 compilerOptions.types = [“vite/client”]
  • 声明一个 vite-env.d.ts 文件 /// <reference types="vite/client" /> 来增加一些 vite 的类型定义补充

3. 样式处理

import '../style/xxx.css';
yarn add -D less
yarn add -D sass
// 组件中使用
import userStyle from './styles/users.module.css';

const User = () => (<div className={userStyle.container}>user</div>)
return defineConfig({
  css: {
    modules: { // css-module 配置,最终会传给 postcss-modules 处理
      scopeBehaviour: 'local'
    },
    postcss: {}, // 内联的 postcss 配置,功能等同 postcss.config.js
    preprocessorOptions: { // css 预处理器的配置
      less: {
        javascriptEnabled: true,
          globalVars: {
            cdnUrl: JSON.stringify('https://www.baidu.com'),
          },
      }
    },
    build: {
      cssCodeSplit: true, // 开启或者禁用,打包时的 css 代码拆分
    }
  }
});

4. 静态资源处理

import imgUrl from './img.png';
import imgUrl2 from '/img2.png'; // 相对于根文件夹的 绝对路径 /img2.png

// 样式
// .bgimg {
//   width: 150px;
//   height: 150px;
//   background-image: url('./huawei.jpeg');
// }

const User = () => (
  <div>
    <img src={imgUrl2} alt="" />
    {/* 如下访问的时 vite /public/img3.png  */} 
    <img src="/img3.png" alt="" />
    <img src={imgUrl} alt="" />
    <div className={RootContainerLess.bgimg} />
  </div> 
)
return defineConfig({
  css: {
    publicDir: 'public', // 可以是文件系统的绝对路径path.resolve(__dirname, 'public') 也可以是相对路径
    assetsInclude: ['**/*.gltf'], // 图片等会被默认当做静态资源,这个选项可以增加更多的静态资源文件类型
    build: {
      assetsInlineLimit: 4096, // 静态资源大小小于这个数值的,会在打包时内联为 base64 ,减少http 请求
    }
  }
});

5. 低版本浏览器兼容

const react = require('@vitejs/plugin-react');
const legacy = require('@vitejs/plugin-legacy');

return defineConfig({
  plugins: [
    react(),
    legacy({
      additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 给 IE11 提供异步支持
    })
  ]
});

6. 给文件夹设置别名 alias

return defineConfig({
  resolve: { // 文件别名
    alias: {
      store: path.join(context, 'src/store')
    },
  },
});

// 业务代码中,可以按照如下方式引用
import globalStore, { Provider } from 'store';

7. 构建阶段传入变量

  1. 借助 vite.config.js 中的配置项 define 来定义一些常量
// vite.config.js 中定义传入的常量
return defineConfig({
  resolve: { // 文件别名
    define: {
      RUNTIME_CONST: JSON.stringify(CONST 常量‘),
    },
  },
});

// d.ts 文件中定义这个常量类型
declare global {
  const RUNTIME_NODE_ENV: string;
}

// 业务代码中使用
console.log('构建阶段传入的常量:', RUNTIME_CONST);
  1. 借助 .env 文件,Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量 --更多查看
  • .env # 所有情况下都会加载
  • .env.local # 所有情况下都会加载,但会被 git 忽略
  • .env.[mode] # 只在指定模式下加载
  • .env.[mode].local # 只在指定模式下加载,但会被 git 忽略

此外 vite 内置了一些环境变量

  • import.meta.env.MODE: {string} 应用运行的模式。
  • import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由base 配置项决定。
  • import.meta.env.PROD: {boolean} 应用是否运行在生产环境。
  • import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)。

.env 文件中定义的环境变量也会挂载到 import.meta.env 对象上

// vite.config.js 中定义 env 文件存放地址及允许解析成常量的前缀
return defineConfig({
  envDir: path.join(context, 'env'), // 加载 .env 文件的目录 默认根文件夹
  envPrefix: 'VITE_ENV_', // .env 文件中环境变量格式
});

// 创建 /env/.env 文件
// 内容:VITE_ENV_XXX=xxx-xxx-xxx # 只有 VITE_ENV_ 为前缀的常量才会挂载到 import.meta.env 对象下

// 配置 TS 类型 vite-env.d.ts 文件中 增加如下
/// <reference types="vite/client" />
interface ImportMetaEnv {
    readonly VITE_ENV_XXX: string
    // 更多环境变量...
}

interface ImportMeta {
    readonly env: ImportMetaEnv
}

// 业务代码中使用注入的常量
console.log(import.meta.env.VITE_ENV_XXX);

8. 自动加载模块,而不必到处 import 模块

// vite.config.js 中使用插件
const inject = require('@rollup/plugin-inject');

return defineConfig({
  plugins: [
    inject({
      exclude: /\.less/,
      include: /\.[tj]sx?$/,
      _: 'lodash', // 自动导入的模块
      React: ['react', '*'],
    }),
    react(),
  ]
});

// 业务代码中即可不 import lodash mobx react 等

9. 给入口 HTML 传入变量

// vite.config.js 中使用插件
const {injectHtml} = require('vite-plugin-html');

return defineConfig({
  plugins: [
    injectHtml({
      data: {
        MAIN_API_URL: 'https://www.baidu.com' // 测试 html 模板传参
      },
    }),
  ]
});

// index.html 文件中使用传参
<html lang="en">
  <body>
    <script>
      var API_URL = '<%- MAIN_API_URL %>';
    </script>
    <div id="root"></div>
    <script type="module" src="./index.tsx"></script>
  </body>
</html>

10. 按需加载

// vite.config.js 中使用插件
const styleImporter = require('vite-plugin-style-import').default;

return defineConfig({
  plugins: [
    styleImporter({
      libs: [
        {
          libraryName: 'antd',
          esModule: true,
          resolveStyle: name => `antd/es/${name}/style/index`
        }
      ]
    }),
    react()
  ]
});

// 业务代码中使用
import { Button } from 'antd';

// 相当于
import { Button } from 'ant-design-vue';
import 'antd/es/button/style/index.js';

11. 代码分离 Code-splitting 及打包后资源规划

// vite.config.js 中使用插件
const styleImporter = require('vite-plugin-style-import').default;

return defineConfig({
  build: {
    outDir: path.resolve(__dirname, 'build'), // 指定输出路径
    assetsDir: 'static', // 一个相对于 outDir 的静态资源输出路径
    cssCodeSplit: true, // 输出的 css 是否是经过 拆分的
    sourcemap: true,
    emptyOutDir: true, // 构建时清空目标文件夹
    chunkSizeWarningLimit: 500, // 生成 chunk 大于这个数值会在控制台warning
    rollupOptions: { // vite 内部使用 rollup 做打包
      output: {
        manualChunks: { // 自定义一些可以共享的 chunk, 默认 node_modules 下的 package 只拆出一个 vendor
          basic: ['react', 'react-dom', 'react-router-dom'],
          vendor: ['antd', 'axios', 'lodash', 'qs']
        },
        chunkFileNames: path.join('static', 'chunk/[name]-[hash].js'),
        entryFileNames: path.join('static', 'js/[name]-[hash].js'),
        assetFileNames: path.join('static', '[ext]/[name]-[hash].[ext]')
      }
    }
  },
});

// 生成的资源结构
.
├── index.html
└── static
    ├── chunk
    │   ├── basic-84f9de30.js
    │   ├── index-0ea6d4a0.js
    │   ├── index-97edd7a4.js
    │   └── vendor-5e54d301.js
    ├── css
    │   ├── index-0fbc2448.css
    │   └── index-697ec425.css
    ├── jpeg
    │   └── huawei-05681d0e.jpeg
    └── js
        └── index-820fccbf.js

12. 构建结果分析

// vite.config.js 中使用插件
const visualizer = require('rollup-plugin-visualizer').default;

return defineConfig({
  plugins: [
    visualizer({
      open: true,
      template: 'treemap',
      gzipSize: true,
      brotliSize: true,
      filename: `report/index.html`
    })
  ]
});

13. eslint 接入

const eslint = require('vite-plugin-eslint').default;
return defineConfig({
  plugins: [
    react(),
    {
      ...eslint({
        fix: false,
        cache: true,
        cacheLocation: path.join(__dirname, 'node_modules/.vite/eslint'),
        formatter: 'stylish',
        throwOnWarning: isDev,
      }), enforce: 'pre' // 在 vite 核心插件执行前执行
    },
  ]
});

14. 开发阶段相关设置

const pkg = require('../package.json');

return defineConfig({
  optimizeDeps: {
    include: Object.keys(pkg.dependencies)
  },
  server: {
    port: parseInt(params['port'], 10) || 3000,
    host: true,
    https: params['https'],
    proxy: {},
    open: true,
    fs: {strict: true},
    hmr: {
      overlay: false
    }
  },
});

2. 测试 vite 过程中遇到的问题

1. 执行 yarn start 后项目是跑起来了,但是控制台有 Failed to load source map 字样的日志

5:58:03 PM [vite] Failed to load source map for /@fs/xxx/node_modules/.vite/vite/hoist-non-react-statics.js?v=684d5287.
5:58:03 PM [vite] Failed to load source map for /@fs/xxx/node_modules/.vite/vite/antd.js?v=684d5287.
Sourcemap for "/xxx/node_modules/react-router-dom/index.js" points to missing source files
Sourcemap for "/xxx/node_modules/react-router/index.js" points to missing source files
Sourcemap for "/xxx/node_modules/async-validator/dist-web/index.js" points to missing source files
return defineConfig({
  optimizeDeps: {
    include: Object.keys(pkg.dependencies); // 这里为了方便将所有 dependencies 的包都加上了
  },
  server: {
   // ...
  },
});
举报

相关推荐

0 条评论