极客油画

preact

使用 Preact,你可以通过组装组件与元素的树形结构来构建用户界面。组件是函数或类,它们返回其树形结构应输出的描述。

这些描述通常用 JSX 或利用标准 JavaScript 标记模板的 HTM 编写,但是推荐使用 JSX 。

要开始使用 Preact,首先需要了解 render()函数。此函数接收一个树形结构描述并创建所描述的结构。接着,它会将该结构附加到作为第二个参数提供的父级 DOM 元素上。

后续对 render()的调用将复用现有的树形结构,并在 DOM 中原位更新。在内部,render()会计算与先前输出结构的差异,以尽可能减少 DOM 操作。

如何使用vite创建preact项目

  1. 准备工作 确保你的系统已安装 Node.js(版本 18+ 或 20+ 推荐)。

  2. 创建项目 打开终端,运行以下命令:

1
npm create vite@latest my-preact-app -- --template preact

命令说明

  • my-preact-app 是你的项目名称,可自行修改。
  • --template preact 指定使用 Vite 官方提供的 Preact 模板。

创建过程中,Vite 会自动生成项目结构和配置文件。

  1. 进入项目并安装依赖
1
2
cd my-preact-app
npm install
  1. 运行开发服务器
1
npm run dev

然后在浏览器打开 http://localhost:5173 即可预览。

  1. 项目构建 构建生产版本:
1
npm run build

构建产物默认输出到 dist 目录。

如何使用webpack和babel直接运行preact项目

  1. 环境准备: 确保你已经安装了 Node.js 和 npm

  2. 初始化项目

创建一个新的项目目录并初始化 package.json 文件:

1
2
3
mkdir my-react-app
cd my-react-app
npm init -y
  1. 安装依赖

安装 React 和 ReactDOM:

1
npm install react@17.0.1 react-dom@17.0.1

安装 Webpack 和 Webpack CLI 和相关插件:

1
2
npm install webpack webpack-cli --save-dev
npm install css-loader style-loader file-loader html-webpack-plugin html-loader --save-dev

安装 Babel 和相关插件:

1
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader --save-dev

@babel/core是babel的核心库,必须安装;@babel/preset-env用于把es6的语法编译成es5的语法;@babel/preset-react用于识别jsx语法;babel-loader用于把不同的文件转化为指定的格式输出。

安装TypeScript和相关插件,如果不安装,代码提示工具就会使用系统全局的,导致版本不对:

1
npm install typescript @types/react@16.9.9 @types/react-dom@16.9.9 --save-dev

安装 Webpack 开发服务器(可选,用于开发环境):

1
npm install webpack-dev-server --save-dev
  1. 配置babel

在项目根目录下创建 .babelrc 文件,并添加以下内容:

1
2
3
{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}
  1. 配置 Webpack

在项目根目录下创建 webpack.config.js 文件,并添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js', // 入口文件
  output: {
    path: path.resolve(__dirname, 'dist'), // 输出目录
    filename: 'bundle.js', // 输出文件名
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/, // 匹配 JavaScript 和 JSX 文件
        exclude: /node_modules/, // 排除 node_modules 目录
        use: {
          loader: 'babel-loader', // 使用 babel-loader 处理
        },
      },
      {
        test: /\.css$/, // 匹配 CSS 文件
        use: ['style-loader', 'css-loader'], // 使用 style-loader 和 css-loader 处理
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.html$/,
        use: {
          loader: 'html-loader'
        }
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      title: 'react app',
      filename: 'index.html',
      template: './src/index.html'
    })
  ],
  devServer: {
    static: path.join(__dirname, 'dist'), // 开发服务器的静态文件目录
    compress: true, // 启用 gzip 压缩
    port: 3000, // 开发服务器端口
  },
  resolve: {
    extensions: ['.js', '.jsx'], // 自动解析的文件扩展名
  },
};
  1. 项目目录结构
my-react-app/
├── src/
│   ├── index.js       // 入口文件
│   ├── App.js         // React 组件
│   └── index.html     // HTML 模板
├── dist/              // Webpack 输出目录
├── .babelrc           // Babel 配置文件
├── webpack.config.js  // Webpack 配置文件
└── package.json       // npm 配置文件

src/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My React App</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

src/index.js (react <=17)

1
2
3
4
5
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

src/index.js (react >= 18)

1
2
3
4
5
6
7
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.getElementById('root'));

root.render(<App />);

src/App.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import React from 'react';

function App() {
  return (
    <div>
      <h1>Hello, React!</h1>
    </div>
  );
}

export default App;
  1. 运行项目

开发环境:

1
npx webpack serve --mode development

打开浏览器访问 http://localhost:3000,即可看到 React 应用。

生产环境:

1
npx webpack build --mode production

编译后的文件会生成在 dist 目录中。

import为何有时必须使用{}

  1. 默认导出(Default Export)
  • 一个模块只能有一个默认导出。
  • 导入时不需要使用 {},且可以自定义导入名称。
  1. 命名导出(Named Export)
  • 一个模块可以有多个命名导出。
  • 导入时必须使用 {},并且名称必须与导出时的名称一致(或者使用 as 重命名)。
  1. 混合使用默认导出和命名导出

示例:混合导出

1
2
3
4
// utils.js
export const add = (a, b) => a + b; // 命名导出
const subtract = (a, b) => a - b;
export default subtract; // 默认导出

导入混合导出

1
2
3
import subtract, { add } from './utils'; // 默认导出不需要 {},命名导出需要 {}
console.log(add(1, 2)); // 输出 3
console.log(subtract(5, 3)); // 输出 2

为何jsx 比 template更好

  1. Vue的单文件组件,使用<template><script>对代码进行分割,直接导致的问题就是上下文丢失。举个例子,你封装了一些常用的函数,在Vue文件中import进来。你这个函数能在template中直接使用吗?
1
2
3
4
// filter.js 文件
export function isNickname(value) {
  return /^[\s\S]{1,50}$/.test(value);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
  <div>
    {{ a }}
    <button @click="a = isNickname('abc')">Test</button>
    {{ b }}
  </div>
</template>
<script>
// eslint-disable-next-line no-unused-vars
import {isNickname} from '../fn/filter';

export default {
  name: 'HelloWorld',
  props: { msg: String },
  data: () => {
    return {
      a: false,
      b: 1,
      c: 1,
    }
  },
  methods: {
    isNickname1() {
      return isNickname('abc');
    }
  }
}
</script>

<style scoped></style>

上述代码会报错:

[Vue warn]: Property or method "isNickname" is not defined on the instance but referenced during render

所以你只能将方法定义在methods中,再引用进来。模板语法并不知道你有isNickname这个函数,简单的操作多了3行代码。

模板语法不是图灵完备的,必须转换为js代码(render 函数),放在component语境下才行。类似的例子还有很多,你会发现,你写的代码与Vue强绑定了,哪天Vue核心库崩了,你代码也崩了。Vue 核心库升级了,周边依赖库也得跟着升级。

  1. 模板分割

好的代码组织能将常变与不变的部分进行分割解耦

Vue 的模板严重限制了这一点。举个例子,前端有个下拉菜单,功能不断增加,而且对于不同的人要显示不同菜单(权限管理)。在 Vue 中,为了实现 html 代码(绑定在 template 中)的分割,你只能再搞一个组件。在 preact 中,可以直接这样写:

1
const menu = <div>abc<div>;

可单独做一个组件(低开销函数组件),也可当做变量,放在当前代码中。相对灵活很多。

JSX 手写 render 渲染函数自带下面的优势:

  • 完整的 js 功能来构建视图页面,可以使用临时变量、js 自带的控制流、以及直接引用当前 js 作用域中的值
  • 开发工具对 jsx 的支持比现有 vue 模板先进(linting、typescript、编译器自动补全)

vite前端打包工具

JavaScript/TypeScript转译工具:

工具 语言实现 相对tsc速度 核心特点 适用场景
Bun Zig 13倍 内置TypeScript编译器、极速启动、一体化工具链 开发环境、快速打包、中小型项目
tsgo (TypeScript 7) Go 10倍 官方原生实现、完全兼容TypeScript 6.0、多线程并行 大型企业项目、需要完全兼容性的场景
Oxc Rust 比SWC快3-5倍 超高性能、轻量级、支持TS/JSX 大规模代码库、高并发开发
SWC Rust 比Babel快5倍以上 快速编译、低内存占用、插件系统 大型应用、构建时间敏感的项目
Babel JavaScript 基准(1倍) 最丰富的插件生态、广泛浏览器支持 需要广泛浏览器兼容、复杂插件需求
tsc (TypeScript 6.0) JavaScript 1倍 最稳定、功能最完整、文档丰富 小型项目、特定TypeScript特性需求

Vite 8 最大的变化是采用了 Rolldown + Oxc 的 Rust 技术栈:

组件 角色 语言 说明
Rolldown 统一打包器 Rust 替代 Rollup,提供 10-30 倍的构建速度提升
Oxc 编译器全家桶 Rust 负责 TypeScript/JSX 解析、转换、压缩等转译工作
Vite 开发服务器/构建协调者 TypeScript 作为上层协调层,提供统一的开发体验

npm使用-D或者--save-dev参数安装完工具后,如何调用这个工具:

npm install -D @typescript/native-preview

npx tsgo -b tsconfig.json
npx tsgo src/index.ts --outDir dist

npm install -D有 @ 和无 @ 的区别

特性 @ 的包 @ 的包
名称格式 package-name @scope/package-name
唯一性 全局唯一 在作用域内唯一
类比 像全球唯一的用户名(如 admin 像“公司/部门-人名”(如 @google/engineer
常见场景 大部分经典库(webpack, axios 1. 大型组织项目(@angular/*, @babel/*
2. 类型定义文件(@types/*
3. 企业的私有包

专题:

本文发表于 2019-06-17,最后修改于 2019-06-17。

本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。


上一篇 « alacritty-tmux 下一篇 » yarn

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image