从写原生JS到玩转框架:我走过的那些弯路和顿悟时刻

作者:良山有风来日期:2025/11/3

还记得刚入行时,我对着满屏的document.getElementById发誓要征服前端。三年后,当我第一次用Vue在半小时内完成过去需要两天的工作时,我才明白:从前端小白到大佬,差的不是代码量,而是思维模式的彻底转变。

今天,我想和你分享这段旅程中的关键转折点。无论你是正在学习前端的新手,还是已经有一定经验的开发者,相信这些感悟都能帮你少走很多弯路。

从“怎么做”到“做什么”:思维的根本转变

刚学JavaScript时,我的脑子里装满了“怎么做”。比如要做一个待办事项应用,我的思路是这样的:

1// 原生JS实现待办事项
2const todoList = document.getElementById('todo-list');
3const input = document.getElementById('todo-input');
4const addButton = document.getElementById('add-button');
5
6// 添加待办事项
7addButton.addEventListener('click', function() {
8  const taskText = input.value.trim();
9  
10  if (taskText) {
11    const listItem = document.createElement('li');
12    listItem.className = 'todo-item';
13    
14    const taskSpan = document.createElement('span');
15    taskSpan.textContent = taskText;
16    
17    const deleteButton = document.createElement('button');
18    deleteButton.textContent = '删除';
19    deleteButton.addEventListener('click', function() {
20      todoList.removeChild(listItem);
21    });
22    
23    listItem.appendChild(taskSpan);
24    listItem.appendChild(deleteButton);
25    todoList.appendChild(listItem);
26    
27    input.value = '';
28  }
29});
30

这段代码逻辑清晰,功能完整,但问题在哪里?我花了大量时间在DOM操作上——创建元素、设置属性、绑定事件、管理父子关系。每个新功能都要重复这些繁琐的操作。

当我第一次接触Vue时,同样的功能变成了这样:

1// Vue 3的实现
2const App = {
3  data() {
4    return {
5      todos: [],
6      newTodo: ''
7    }
8  },
9  methods: {
10    addTodo() {
11      if (this.newTodo.trim()) {
12        this.todos.push({
13          id: Date.now(),
14          text: this.newTodo,
15          completed: false
16        });
17        this.newTodo = '';
18      }
19    },
20    removeTodo(id) {
21      this.todos = this.todos.filter(todo => todo.id !== id);
22    }
23  },
24  template: `
25    <div>
26      <input v-model="newTodo" @keyup.enter="addTodo">
27      <button @click="addTodo">添加</button>
28      <ul>
29        <li v-for="todo in todos" :key="todo.id">
30          {{ todo.text }}
31          <button @click="removeTodo(todo.id)">删除</button>
32        </li>
33      </ul>
34    </div>
35  `
36};
37

发现区别了吗?我不再关心“怎么创建DOM元素”、“怎么绑定事件”,而是专注于“数据是什么”、“用户要做什么”。这种从“怎么做”到“做什么”的转变,就是前端开发思维的第一个分水岭。

数据驱动:从视图优先到状态优先

在原生JS时代,我们往往是视图优先的思维——先考虑页面上有什么元素,然后想办法操作它们。而在框架时代,我们变成了状态优先。

让我用一个更实际的例子说明。假设我们要做一个购物车功能,在原生JS中可能是这样的:

1// 原生JS购物车
2let cartCount = 0;
3const cartButton = document.getElementById('cart-button');
4const countSpan = document.getElementById('cart-count');
5
6function updateCartUI() {
7  countSpan.textContent = cartCount;
8  cartButton.style.backgroundColor = cartCount > 0 ? 'red' : 'gray';
9}
10
11document.querySelectorAll('.add-to-cart').forEach(button => {
12  button.addEventListener('click', function() {
13    cartCount++;
14    updateCartUI();
15  });
16});
17

这里的问题是,数据和UI是强耦合的。每次数据变化,我都需要手动调用updateCartUI来同步视图。

而在React中,同样的逻辑变得异常简单:

1// React购物车组件
2import { useState } from 'react';
3
4function ShoppingCart() {
5  const [cartCount, setCartCount] = useState(0);
6  
7  const addToCart = () => {
8    setCartCount(prevCount => prevCount + 1);
9  };
10  
11  return (
12    <div>
13      <button 
14        style={{ backgroundColor: cartCount > 0 ? 'red' : 'gray' }}
15      >
16        购物车 ({cartCount})
17      </button>
18      
19      {products.map(product => (
20        <div key={product.id}>
21          <h3>{product.name}</h3>
22          <button onClick={addToCart}>加入购物车</button>
23        </div>
24      ))}
25    </div>
26  );
27}
28

框架自动处理了数据和视图的同步。当cartCount变化时,React会自动重新渲染相关的组件部分。我不再需要手动操作DOM,只需要关心状态如何变化。

这种思维转变带来的最大好处是:代码更可预测、更易于维护。因为UI只是状态的函数,相同的状态总是产生相同的UI。

组件化思维:从页面到乐高积木

在jQuery时代,我们往往按页面来组织代码。一个页面就是一个巨大的JavaScript文件,里面塞满了各种事件处理函数和DOM操作。

现在,我们用的是组件化思维。把UI拆分成独立的、可复用的组件,就像搭乐高积木一样。

让我用2025年最流行的Vue 3组合式API来展示组件化的威力:

1// 一个可复用的Modal组件
2import { ref, computed } from 'vue';
3
4// 模态框组件
5const Modal = {
6  props: {
7    title: String,
8    show: Boolean
9  },
10  emits: ['update:show'],
11  setup(props, { emit }) {
12    const isShowing = computed({
13      get: () => props.show,
14      set: (value) => emit('update:show', value)
15    });
16    
17    const close = () => {
18      isShowing.value = false;
19    };
20    
21    return { isShowing, close };
22  },
23  template: `
24    <div v-if="isShowing" class="modal-overlay" @click="close">
25      <div class="modal-content" @click.stop>
26        <div class="modal-header">
27          <h2>{{ title }}</h2>
28          <button @click="close">&times;</button>
29        </div>
30        <div class="modal-body">
31          <slot></slot>
32        </div>
33      </div>
34    </div>
35  `
36};
37
38// 使用Modal组件
39const UserProfile = {
40  components: { Modal },
41  data() {
42    return {
43      showEditModal: false,
44      user: { name: '张三', email: 'zhangsan@example.com' }
45    }
46  },
47  template: `
48    <div>
49      <h1>用户资料</h1>
50      <p>姓名: {{ user.name }}</p>
51      <p>邮箱: {{ user.email }}</p>
52      <button @click="showEditModal = true">编辑资料</button>
53      
54      <Modal 
55        title="编辑资料" 
56        :show="showEditModal"
57        @update:show="val => showEditModal = val"
58      >
59        <form>
60          <input v-model="user.name">
61          <input v-model="user.email">
62          <button type="submit">保存</button>
63        </form>
64      </Modal>
65    </div>
66  `
67};
68

这种组件化思维让我们的代码就像搭积木一样简单。每个组件都有明确的职责和接口,可以独立开发、测试和复用。

状态管理:从全局变量到专业工具

随着应用复杂度提升,状态管理成为必须面对的问题。在小型应用中,可能用组件内状态就足够了。但在大型应用中,我们需要更专业的状态管理方案。

2025年的状态管理已经有了很多成熟的选择。让我用Pinia(Vue的官方状态管理库)来展示现代状态管理的思路:

1// store/userStore.js - 用户状态管理
2import { defineStore } from 'pinia';
3
4export const useUserStore = defineStore('user', {
5  state: () => ({
6    user: null,
7    isLoading: false,
8    error: null
9  }),
10  
11  getters: {
12    isLoggedIn: (state) => !!state.user,
13    userName: (state) => state.user?.name || '游客'
14  },
15  
16  actions: {
17    async login(credentials) {
18      this.isLoading = true;
19      this.error = null;
20      
21      try {
22        // 模拟API调用
23        const response = await fetch('/api/login', {
24          method: 'POST',
25          body: JSON.stringify(credentials)
26        });
27        
28        if (response.ok) {
29          this.user = await response.json();
30        } else {
31          throw new Error('登录失败');
32        }
33      } catch (error) {
34        this.error = error.message;
35      } finally {
36        this.isLoading = false;
37      }
38    },
39    
40    logout() {
41      this.user = null;
42    }
43  }
44});
45
46// 在组件中使用
47const LoginComponent = {
48  setup() {
49    const userStore = useUserStore();
50    const email = ref('');
51    const password = ref('');
52    
53    const handleLogin = async () => {
54      await userStore.login({
55        email: email.value,
56        password: password.value
57      });
58      
59      if (userStore.isLoggedIn) {
60        // 登录成功,跳转到首页
61        router.push('/dashboard');
62      }
63    };
64    
65    return {
66      email,
67      password,
68      handleLogin,
69      isLoading: computed(() => userStore.isLoading),
70      error: computed(() => userStore.error)
71    };
72  },
73  template: `
74    <div>
75      <input v-model="email" placeholder="邮箱">
76      <input v-model="password" type="password" placeholder="密码">
77      <button @click="handleLogin" :disabled="isLoading">
78        {{ isLoading ? '登录中...' : '登录' }}
79      </button>
80      <p v-if="error" class="error">{{ error }}</p>
81    </div>
82  `
83};
84

这种集中式的状态管理让数据流变得清晰可预测。无论组件在树的哪个位置,都能访问和修改同一份状态,而且所有的修改都是可追踪的。

工具链思维:从手动配置到开箱即用

还记得当年手动配置webpack的日子吗?现在,我们有了更强大的工具链思维。

2025年的前端工具链已经高度集成化。以Vite为例,它提供了开箱即用的开发体验:

1// vite.config.js - 现代构建配置
2import { defineConfig } from 'vite';
3import vue from '@vitejs/plugin-vue';
4
5export default defineConfig({
6  plugins: [vue()],
7  
8  // 开发服务器配置
9  server: {
10    port: 3000,
11    proxy: {
12      '/api': {
13        target: 'http://localhost:8080',
14        changeOrigin: true
15      }
16    }
17  },
18  
19  // 构建配置
20  build: {
21    outDir: 'dist',
22    sourcemap: true,
23    rollupOptions: {
24      output: {
25        manualChunks: {
26          vendor: ['vue', 'vue-router', 'pinia']
27        }
28      }
29    }
30  },
31  
32  // CSS预处理
33  css: {
34    preprocessorOptions: {
35      scss: {
36        additionalData: `@import "@/styles/variables.scss";`
37      }
38    }
39  }
40});
41

工具链的进步让我们能专注于业务逻辑,而不是构建配置。热重载、TypeScript支持、代码分割、优化打包,这些都成了基础设施。

类型思维:从运行时错误到编译时检查

JavaScript的灵活性是一把双刃剑。为了解决类型安全问题,TypeScript已经成为2025年前端开发的事实标准。

让我们看看TypeScript如何提升代码质量:

1// 用户相关类型定义
2interface User {
3  id: number;
4  name: string;
5  email: string;
6  role: UserRole;
7}
8
9type UserRole = 'admin' | 'user' | 'guest';
10
11// API响应类型
12interface ApiResponse<T> {
13  data: T;
14  message: string;
15  success: boolean;
16}
17
18// 用户服务
19class UserService {
20  async getUserById(id: number): Promise<ApiResponse<User>> {
21    const response = await fetch([`/api/users/${id}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.users.md));
22    
23    if (!response.ok) {
24      throw new Error(`获取用户失败: ${response.status}`);
25    }
26    
27    const result: ApiResponse<User> = await response.json();
28    return result;
29  }
30  
31  async updateUser(user: Partial<User>): Promise<ApiResponse<User>> {
32    const response = await fetch([`/api/users/${user.id}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.users.md), {
33      method: 'PATCH',
34      headers: { 'Content-Type': 'application/json' },
35      body: JSON.stringify(user)
36    });
37    
38    const result: ApiResponse<User> = await response.json();
39    return result;
40  }
41}
42
43// 在React组件中使用
44const UserProfile: React.FC<{ userId: number }> = ({ userId }) => {
45  const [user, setUser] = useState<User | null>(null);
46  const [loading, setLoading] = useState(true);
47  
48  useEffect(() => {
49    const userService = new UserService();
50    
51    userService.getUserById(userId)
52      .then(response => {
53        if (response.success) {
54          setUser(response.data);
55        }
56      })
57      .finally(() => setLoading(false));
58  }, [userId]);
59  
60  if (loading) return <div>加载中...</div>;
61  if (!user) return <div>用户不存在</div>;
62  
63  return (
64    <div>
65      <h1>{user.name}</h1>
66      <p>{user.email}</p>
67      <p>角色: {user.role}</p>
68    </div>
69  );
70};
71

TypeScript在编译时就能发现很多潜在错误,提供更好的代码提示,让重构变得安全。这种类型思维让我们从“写时一时爽,调试火葬场”变成了“写时多思考,调试少烦恼”。

响应式思维:从同步到异步

现代前端应用充满了异步操作——API调用、用户交互、定时任务等。响应式编程思维帮助我们更好地处理这些异步数据流。

让我们用RxJS来感受一下响应式思维的魅力:

1// 搜索自动完成功能
2import { fromEvent } from 'rxjs';
3import { debounceTime, distinctUntilChanged, switchMap, map } from 'rxjs/operators';
4
5class SearchService {
6  constructor() {
7    this.setupSearch();
8  }
9  
10  setupSearch() {
11    const searchInput = document.getElementById('search-input');
12    
13    // 创建搜索输入的数据流
14    const search$ = fromEvent(searchInput, 'input').pipe(
15      map(event => event.target.value.trim()),
16      debounceTime(300), // 防抖300ms
17      distinctUntilChanged(), // 值真正变化时才触发
18      switchMap(query => this.searchAPI(query)) // 取消之前的请求
19    );
20    
21    // 订阅搜索结果
22    search$.subscribe({
23      next: results => this.displayResults(results),
24      error: err => console.error('搜索失败:', err)
25    });
26  }
27  
28  async searchAPI(query) {
29    if (!query) return [];
30    
31    const response = await fetch([`/api/search?q=${encodeURIComponent(query)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.q.md));
32    if (!response.ok) throw new Error('搜索失败');
33    
34    return await response.json();
35  }
36  
37  displayResults(results) {
38    const resultsContainer = document.getElementById('search-results');
39    resultsContainer.innerHTML = results
40      .map(item => [`<div class="result-item">${item.name}</div>`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.div.md))
41      .join('');
42  }
43}
44

这种响应式思维让我们能够以声明式的方式处理复杂的事件流和异步操作,代码更加简洁和健壮。

测试思维:从手动测试到自动化保障

在原生JS时代,测试往往是被忽视的一环。而现在,测试思维已经成为专业前端开发的标配。

让我展示一下现代前端测试的最佳实践:

1// UserProfile.test.js - Vue组件测试
2import { mount } from '@vue/test-utils';
3import UserProfile from './UserProfile.vue';
4import { useUserStore } from '@/stores/user';
5
6// 模拟Pinia store
7jest.mock('@/stores/user', () => ({
8  useUserStore: jest.fn()
9}));
10
11describe('UserProfile', () => {
12  let mockStore;
13  
14  beforeEach(() => {
15    mockStore = {
16      user: { name: '测试用户', email: 'test@example.com' },
17      updateProfile: jest.fn().mockResolvedValue({ success: true })
18    };
19    
20    useUserStore.mockReturnValue(mockStore);
21  });
22  
23  it('应该正确显示用户信息', () => {
24    const wrapper = mount(UserProfile);
25    
26    expect(wrapper.find('.user-name').text()).toBe('测试用户');
27    expect(wrapper.find('.user-email').text()).toBe('test@example.com');
28  });
29  
30  it('点击编辑按钮应该打开模态框', async () => {
31    const wrapper = mount(UserProfile);
32    
33    await wrapper.find('.edit-button').trigger('click');
34    
35    expect(wrapper.find('.edit-modal').exists()).toBe(true);
36  });
37  
38  it('提交表单应该调用store更新方法', async () => {
39    const wrapper = mount(UserProfile);
40    
41    // 打开编辑模态框
42    await wrapper.find('.edit-button').trigger('click');
43    
44    // 修改表单
45    await wrapper.find('#name-input').setValue('新用户名');
46    await wrapper.find('form').trigger('submit');
47    
48    expect(mockStore.updateProfile).toHaveBeenCalledWith({
49      name: '新用户名',
50      email: 'test@example.com'
51    });
52  });
53});
54
55// 工具函数测试
56import { formatDate, calculateAge } from './dateUtils';
57
58describe('dateUtils', () => {
59  describe('formatDate', () => {
60    it('应该正确格式化日期', () => {
61      const date = new Date('2023-05-15');
62      expect(formatDate(date)).toBe('2023年5月15日');
63    });
64    
65    it('处理无效日期应该返回空字符串', () => {
66      expect(formatDate('invalid')).toBe('');
67    });
68  });
69  
70  describe('calculateAge', () => {
71    it('应该正确计算年龄', () => {
72      const birthDate = new Date('1990-01-01');
73      const currentDate = new Date('2023-12-01');
74      
75      expect(calculateAge(birthDate, currentDate)).toBe(33);
76    });
77  });
78});
79

测试思维让我们在代码变更时更有信心,也促进了更好的代码设计——可测试的代码往往是设计良好的代码。

性能思维:从后知后觉到主动优化

在框架时代,性能优化不再是事后考虑,而是开发过程中就要思考的问题。

让我们看看2025年的一些性能优化实践:

1// React懒加载和代码分割
2import { lazy, Suspense } from 'react';
3
4// 懒加载重组件
5const HeavyComponent = lazy(() => import('./HeavyComponent'));
6const DashboardCharts = lazy(() => import('./DashboardCharts'));
7
8function App() {
9  return (
10    <div>
11      <Suspense fallback={<div>加载中...</div>}>
12        <Routes>
13          <Route path="/dashboard" element={
14            <DashboardLayout>
15              <DashboardCharts />
16            </DashboardLayout>
17          } />
18          <Route path="/reports" element={<HeavyComponent />} />
19        </Routes>
20      </Suspense>
21    </div>
22  );
23}
24
25// 虚拟滚动优化长列表
26import { FixedSizeList as List } from 'react-window';
27
28function BigList({ items }) {
29  const Row = ({ index, style }) => (
30    <div style={style}>
31      <div className="list-item">
32        <span>{items[index].name}</span>
33        <button>操作</button>
34      </div>
35    </div>
36  );
37  
38  return (
39    <List
40      height={400}
41      itemCount={items.length}
42      itemSize={50}
43    >
44      {Row}
45    </List>
46  );
47}
48
49// 使用useMemo和useCallback避免不必要的重渲染
50function ExpensiveComponent({ data, onUpdate }) {
51  // 缓存计算结果
52  const processedData = useMemo(() => {
53    return data.map(item => ({
54      ...item,
55      score: calculateComplexScore(item)
56    }));
57  }, [data]);
58  
59  // 缓存回调函数
60  const handleUpdate = useCallback((newValue) => {
61    onUpdate(processedData.id, newValue);
62  }, [processedData.id, onUpdate]);
63  
64  return (
65    <div>
66      {processedData.map(item => (
67        <ExpensiveChild 
68          key={item.id} 
69          data={item}
70          onUpdate={handleUpdate}
71        />
72      ))}
73    </div>
74  );
75}
76

性能思维让我们在享受框架便利的同时,也能构建出高效的应用。

回顾与展望

从原生JS到现代框架,这场思维转变的核心是什么?我认为是抽象层次的提升

我们不再关心底层的DOM操作,而是专注于业务逻辑和用户体验。我们不再手动管理状态同步,而是依赖框架的响应式系统。我们不再从零开始搭建项目,而是站在巨人肩膀上。

但重要的是,框架只是工具,思维才是核心。理解框架背后的原理,知道在什么场景下该用什么解决方案,这种能力比掌握某个具体框架更重要。

2025年的前端生态还在快速演进,但有些趋势已经清晰:TypeScript的普及、构建工具的趋同、全栈框架的兴起、AI辅助开发的成熟……

作为前端开发者,我们的学习之路永无止境。但只要你掌握了正确的思维模式,无论技术如何变化,你都能快速适应并保持竞争力。

你现在处于哪个阶段呢?是还在原生JS的海洋中挣扎,还是已经在框架的世界里游刃有余?无论哪种,我都希望这篇文章能给你一些启发。前端的世界很精彩,值得我们一直探索下去。

欢迎在评论区分享你的前端学习故事,我们一起进步!


从写原生JS到玩转框架:我走过的那些弯路和顿悟时刻》 是转载文章,点击查看原文


上一篇:hive的SQL练习3

下一篇:CSS 的网格布局

相关推荐


Redis(95)Redis的防火墙配置如何设置?
Victor3562025/10/31

设置Redis的防火墙配置是确保Redis实例安全的一个关键步骤。正确配置防火墙可以防止未经授权的访问,减少潜在的安全漏洞。以下是如何在不同环境中进行防火墙配置的详细指南。 1. 使用iptables配置防火墙(Linux) 步骤 1: 安装iptables 大多数现代Linux发行版都预装了iptables。如果没有安装,可以使用以下命令进行安装: sudo apt-get install iptables # 在Debian/Ubuntu sudo yum install iptables


MySQL 索引原理
洲覆2025/10/28

文章目录 一、索引1.1 索引分类1.2 主键选择🌟 二、约束2.1 外键约束2.2 约束与索引的区别 三、索引实现原理3.1 索引存储层级结构3.2 B+ 树B+ 树层高问题🌟关于自增 ID 四、索引类型4.1 聚集索引4.2 辅助索引 一、索引 在数据库中,索引是提高查询性能的关键机制。它相当于书籍的目录,通过索引可以快速定位到数据在磁盘中的位置,从而减少 I/O 操作。对于 InnoDB 而言,索引不仅影响查询性能,还决定了数据在物理层的存储结构。 1


Python 的内置函数 delattr
IMPYLH2025/10/25

Python 内建函数列表 > Python 的内置函数 delattr def delattr(obj, name:str): ''' 删除指定的属性 :param obj: 一个对象 :param name: 要删除的属性的名字 ''' Python 的内置函数 delattr 用于动态删除对象的属性。该函数需要两个参数:第一个参数是目标对象,第二个参数是要删除的属性名称(字符串形式)。 示例 运行 class Person: d


c++算法题目总结
July尘2025/10/23

5分题 001 Hello World(输出语句) #include<stdio.h> int main(){ printf("Hello World!"); return 0; } 004计算摄氏温度(简单计算) #include<stdio.h> int main(){ int F; scanf("%d",&F); int C = 5 * (F-32) / 9; printf("Celsius = %d",C);


【自然资源】自然资源系统业务全梳理,点赞收藏
jr4282025/10/22

自然资源系统业务全梳理 结合为自然部门的业务和多方资料来源,对自然资源业务体系和信息化做了梳理,使测绘地理信息行业在自然资源领域如何落地变得具象化。 自然资源管理业务框架的共性特征 分为基础性业务、核心业务与综合性业务3类。 基础性业务包括调查监测,确权登记,主要是摸清家底,确定权属,统一底图底数,由原来部门分治管理时土地、森林等分头调查确权统一到以三调为基础的自然资源一张底图, 测绘和地理信息业务主要是建立自然资源及国土空间数据基准,进行数据采集,并提供信息化技术支撑。 核心业务主要包括所有权


将 EasySQLite 解决方案文件格式从 .sln 升级为更简洁的 .slnx
追逐时光者2025/10/20

前言 EasySQLite 是一个 .NET 9 操作 SQLite 入门到实战详细教程,主要是对学校班级,学生信息进行管理维护。本文的主要内容是将 EasySQLite 项目解决方案文件格式从 .sln 格式升级为更简洁的 .slnx 格式。 GitHub开源地址:github.com/YSGStudyHar… 选型、开发、部署详细教程 第一天、SQLite 简介 第二天、在 Windows 上配置 SQLite环境 第三天、SQLite快速入门 第四天、EasySQLite前后端项目框


汽车免拆诊断案例 | 2014 款宝马 M4 车冷起动后发动机抖动
虹科Pico汽车示波器2025/10/19

故障现象  一辆2014款宝马M4车,搭载S55B30A发动机,累计行驶里程约为9.4万km。车主反映,冷起动后发动机抖动严重,且组合仪表上的发动机故障灯异常点亮;行驶一段距离后熄火再重新起动发动机,发动机工作恢复正常。 故障诊断 接车后试车,发动机工作正常。用故障检测仪检测,发动机控制单元(DME)中存储有气缸5和气缸6间歇失火的故障代码(图1)。 图1 DME中存储的故障代码 将车停放一晚,第二天早上试车,同时用虹科Pico汽车示波器测量相关信号波形。由图2可知,冷起动后发


最强流式渲染,没有之一
Simon_He2025/10/18

vue-markdown-renderer 阮一峰推荐,性能比vercel出品streamdown 好 100 倍,性能以及渲染表现一定是市面上所有流式渲染中最好的一档,视频中会去讲解为什么,视频比较干,建议仔细看完到最后,希望对于ai相关的小伙伴有一定的收获,谢谢~ 下面有详细分析市面上的 ai 渲染,以及 vercel 的 streamdown 和如何优化的细节,在面对现阶段 ai 的岗位越来越多,作为前端,掌握 ai 流式渲染,也可以成为你面试的亮点 【最强流式渲染没有之一】 www.bi


开源 ETL(Extract,Transform,Load)工具之Apache Hop
西京刀客2025/10/16

文章目录 开源 ETL(Extract,Transform,Load)工具之Apache HopApache Hop起源与背景什么是Apache Hopapache hop 核心概念Tools(工具)Item types(条目类型)Projects(项目)关键概念对比与应用建议 术语对照下载安装使用在 Hop GUI 中创建 Pipeline(步骤) Apache Hop 的局限Apache Hop 的外部插件仓库(hop-plugins)参考 开源 ETL(Extrac


Subword算法之WordPiece、Unigram与SentencePiece
红纸2812025/10/15

在昨天的内容里,我们已经围绕 Subword 分词体系中最基础的两类算法 ——BPE(字节对编码)与 BBPE(双向字节对编码)展开了详细拆解:从 BPE 的 “训练 - 编码” 核心逻辑切入,用具体案例还原了它如何通过迭代合并高频字节对构建词汇表,也分析了其在控制词汇表规模、处理未登录词上的优势与局限;随后又聚焦 BBPE 与 BPE 的差异,通过预处理、多轮字节对统计与合并的完整实例,清晰呈现了 BBPE 的工作原理,以及它在特定场景下的应用价值。 但 Subword 分词的技术版图远不止

上一篇:hive的SQL练习3

下一篇:CSS 的网格布局

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2025 聚合阅读