介
我比較喜歡使用 Vue 來開發,所以有時會深入研究其功能和特性。通過這篇文章,向你們介紹十個很酷的竅門和技巧,以幫助大家成為更好的 Vue 開發者。
隨著Vue 2.6的推出,已經引入了插槽的簡寫方式,之前簡寫可用于事件(例如,@click表示v-on:click事件)或冒號表示方式用于綁定(:src)。例如,如果有一個表格組件,則可以按以下方式使用此功能:
如果要在created或mounted方法中定義自定義事件偵聽器或第三方插件,并且需要在beforeDestroy方法中將其刪除以免引起任何內存泄漏,則可以使用此功能。使用$on(‘hook:’)方法,我們可以僅使用一種生命周期方法(而不是兩種)來定義/刪除事件。
你可能已經知道可以將props驗證為原始類型,例如字符串,數字甚至對象。我們還可以使用自定義驗證器,例如,如果要針對字符串列表進行驗證:
Vue 2.6 的最酷功能之一是可以將指令參數動態傳遞給組件。假設有一個按鈕組件,并且在某些情況下想監聽單擊事件,而在其他情況下想監聽雙擊事件。這就是動態指令派上用場的地方了:
有時,我們不同路由共用某些時,如果在這些路由之間切換,則默認情況下,共享組件將不會重新渲染,因為Vue 出于性能原因會重用該組件。但是,如果我們仍然希望重新渲染這些組件,則可以通過在路由器視圖組件中提供:key屬性來實現重新渲染。
這是一個非常酷的功能,可將所有prop從父組件傳遞到子組件。如果我們有另一個組件的包裝器組件,這將特別方便。因為,我們不必一個一個將prop傳遞給子組件,而是一次傳遞所有prop:
上面的可以代替下面的做法
如果子組件不在父組件的根目錄下,則可以將所有事件偵聽器從父組件傳遞到子組件,如下所示:
如果子組件位于其父組件的根目錄下,則默認情況下它將獲得那些組件,因此不需要此小技巧。
默認情況下,每個Vue實例都可以訪問$createElement方法來創建和返回虛擬節點。例如,可以利用它在可以通過v-html指令傳遞的方法中使用標記。在函數組件中,可以將此方法作為渲染函數中的第一個參數訪問。
由于Vue CLI 3默認支持使用JSX,因此現在(如果愿意)我們可以使用JSX編寫代碼(例如,可以方便地編寫函數組件)。如果尚未使用Vue CLI 3,則可以使用babel-plugin-transform-vue-jsx獲得JSX支持。
默認情況下,v-model是@input事件監聽器和:value props上的語法糖。但是,我們可以在Vue組件中指定一個模型屬性,以定義使用什么事件和值
希望這些竅門和技巧對你有所幫助,如果你也知道哪些技巧,歡迎留言。
原文:https://www.telerik.com/blogs/12-tips-and-tricks-to-improve-your-vue-projects
前端開發中,React 組件是我們日常開發的基礎,而使用 TypeScript 泛型能讓這些組件更加靈活和可重用。今天我們就來聊聊如何利用 TypeScript 泛型創建可重用的 React 組件。
在深入具體操作之前,先簡單介紹一下泛型的概念。泛型允許你在定義組件時不指定具體的數據類型,而是在使用組件時再指定具體的類型。這樣一來,我們的組件就能夠適應多種數據類型,不必為每種數據類型分別創建不同的組件。
市面上已經有很多關于 TypeScript 泛型的文章和教程,所以本文將聚焦于如何在 React 組件中使用泛型,讓你的組件變得更加靈活和可重用。
接下來,我們將通過實例代碼一步步展示如何實現這一目標,讓你能夠輕松掌握這項技能,并應用到實際項目中去。無論你是剛入門的新手,還是有一定經驗的開發者,相信都能從中受益。準備好了嗎?讓我們開始吧!
創建一個簡單的泛型 React 組件
首先,我們來創建一個泛型 React 組件,它可以接受任何類型的數據并通過一個渲染函數將數據展示出來。
// 定義一個泛型類型的 props
type Props<T> = {
data: T
render: (data: T) => React.ReactNode
}
// 創建一個泛型 React 組件
export function GenericComponent<T>({ data, render }: Props<T>) {
return <div>{render(data)}</div>
}
在這個例子中,GenericComponent 接受一個 render 屬性,這個屬性是一個函數,它接收類型為 T 的數據并返回一個 React.ReactNode。這種模式通常被稱為“render props”,它可以讓你更靈活地控制數據的渲染方式。
使用泛型組件渲染字符串
接下來,我們用一個字符串類型的數據來使用這個泛型組件。
import GenericComponent from './GenericComponent'
function RenderString() {
return (
<GenericComponent<string>
data="Hello, world!"
render={(data) => <span>{data.toUpperCase()}</span>}
/>
)
}
在這個例子中,我們明確地告訴 GenericComponent 預期接收一個字符串類型的數據。渲染函數將字符串轉換為大寫,并且 TypeScript 確保了在 render 屬性中進行的操作是對字符串類型數據有效的。
使用自定義類型的數據
現在我們用一個自定義類型的數據來使用泛型組件。
type Person = {
name: string
age: number
}
import GenericComponent from './GenericComponent'
function RenderPerson() {
return (
<GenericComponent<Person>
data={{ name: 'Alice', age: 30 }}
render={(data) => (
<span>
{data.name} is {data.age} years old
</span>
)}
/>
)
}
在這個例子中,TypeScript 確保 data 屬性的數據類型與 render 函數中預期的類型匹配。
使用泛型組件渲染任務列表
最后,我們來看看如何用泛型組件渲染一個任務列表。
import { useState } from 'react'
import GenericComponent from './GenericComponent'
interface TodoItem {
id: number
title: string
completed: boolean
}
export function TodoList() {
// 初始任務列表
const initialTodos: TodoItem[] = [
{ id: 1, title: 'Learn React', completed: false },
{ id: 2, title: 'Write blog post', completed: true },
{ id: 3, title: 'Study TypeScript', completed: false },
]
// 用于追蹤任務列表的狀態
const [todos, setTodos] = useState<TodoItem[]>(initialTodos)
// 切換任務完成狀態的函數
function toggleTodoCompletion(id: number) {
const updatedTodos = todos.map((todo) =>
(todo.id === id ? { ...todo, completed: !todo.completed } : todo))
setTodos(updatedTodos)
}
// 任務列表的復雜渲染函數
function renderTodos(todos: TodoItem[]) {
return (
<ul>
{todos.map((todo) => (
<li
key={todo.id}
style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer' }}
onClick={() => toggleTodoCompletion(todo.id)}
>
{todo.title}
</li>
))}
</ul>
)
}
return <GenericComponent<TodoItem[]>
data={todos}
render={renderTodos}
/>
}
在這個例子中,我們創建了一個 TodoList 組件,它使用 GenericComponent 渲染一個任務列表。渲染函數更加復雜,因為它需要處理一個項目列表。TypeScript 確保 data 屬性的數據類型與 render 函數中預期的類型匹配。
在實際開發中,很多時候我們需要從 API 獲取數據并展示在頁面上。利用 TypeScript 泛型,我們可以創建一個通用的 React 組件來處理這種情況。這樣不僅能提高代碼的可重用性,還能使組件更加靈活。今天我們就通過一個例子來展示如何實現這一目標。
創建一個用于獲取數據的泛型 React 組件
首先,我們創建一個泛型組件 FetchAndDisplay,它可以從指定的 URL 獲取數據,并通過一個渲染函數將數據展示出來。
import { useEffect, useState } from 'react'
// 定義一個泛型類型的 props
type Props<T> = {
url: string
render: (data: T) => React.ReactNode
}
// 創建一個泛型 React 組件
export function FetchAndDisplay<T>({ url, render }: Props<T>) {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error('Failed to fetch data')
}
const json = await response.json()
setData(json)
} catch (error) {
setError(error)
} finally {
setLoading(false)
}
}
fetchData()
}, [url])
if (loading) {
return <div>Loading...</div>
}
if (error) {
return <div>Error: {error.message}</div>
}
if (data) {
return <div>{render(data)}</div>
}
return null
}
在這個例子中,FetchAndDisplay 是一個泛型組件,它接受一個 URL 和一個渲染函數。組件使用 fetch 方法從指定的 URL 抓取數據,并在抓取成功后調用渲染函數來展示數據。同時,組件還處理了加載和錯誤狀態。
使用 FetchAndDisplay 組件獲取和展示帖子
接下來,我們使用 FetchAndDisplay 組件來取取并展示一組帖子數據。
import { FetchAndDisplay } from './FetchAndDisplay'
type Post = {
userId: number
id: number
title: string
body: string
}
function RenderPosts(posts: Post[]) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</li>
))}
</ul>
)
}
export function PostList() {
return <FetchAndDisplay<Post[]>
url='https://jsonplaceholder.typicode.com/posts'
render={RenderPosts}
/>
}
在這個例子中,我們使用 FetchAndDisplay 組件從 JSONPlaceholder API 獲取一組帖子數據,并通過 RenderPosts 函數將其展示出來。
使用 FetchAndDisplay 組件獲取和展示用戶
同樣地,我們可以使用同一個 FetchAndDisplay 組件來抓取和展示用戶數據。
import { FetchAndDisplay } from './FetchAndDisplay'
type User = {
id: number
name: string
email: string
}
function RenderUsers(users: User[]) {
return (
<ul>
{users.map((user) => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
)
}
export function UserList() {
return <FetchAndDisplay<User[]>
url='https://jsonplaceholder.typicode.com/users'
render={RenderUsers}
/>
}
在這個例子中,我們使用 FetchAndDisplay 組件從 JSONPlaceholder API 獲取一組用戶數據,并通過 RenderUsers 函數將其展示出來。這展示了泛型在 React 組件中的強大作用,我們可以用同一個組件處理不同類型的數據獲取和展示。
在實際開發中,表單是我們常用的組件之一。為了提升代碼的復用性和靈活性,我們可以使用 TypeScript 泛型創建一個通用的表單組件。盡管在實際項目中我們通常會使用像 Formik 或 react-hook-form 這樣的庫來處理表單,但為了演示泛型的強大之處,我們將從頭開始創建一個簡單的表單組件。
定義表單字段和組件的類型
首先,我們定義一些 TypeScript 類型,用來指定表單字段的結構以及我們的通用表單組件將接受的 props。這些類型確保了類型安全,并幫助我們管理表單的狀態和行為。
type FormField = {
name: string
label: string
type: 'text' | 'email' | 'password' // 可以根據需要擴展
}
type GenericFormProps<T> = {
fields: FormField[]
initialValues: T
onSubmit: (values: T) => void
}
創建通用表單組件
接下來,我們創建一個函數組件,它接受字段、初始值和一個提交處理函數作為參數。
import { useState } from 'react'
export function GenericForm<T>({ fields, initialValues, onSubmit }: GenericFormProps<T>) {
const [values, setValues] = useState<T>(initialValues)
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const { name, value } = e.target
setValues((prevValues) => ({ ...prevValues, [name]: value }))
}
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
onSubmit(values)
}
return (
<form onSubmit={handleSubmit}>
{fields.map((field) => (
<div key={field.name}>
<label htmlFor={field.name}>{field.label}</label>
<input type={field.type} name={field.name}
value={(values as any)[field.name]}
onChange={handleChange} />
</div>
))}
<button type='submit'>Submit</button>
</form>
)
}
使用通用表單組件
最后,我們使用通用表單組件,并指定具體的表單字段和初始值。
type UserFormValues = {
name: string
email: string
password: string
}
const userFormFields: FormField[] = [
{ name: 'name', label: 'Name', type: 'text' },
{ name: 'email', label: 'Email', type: 'email' },
{ name: 'password', label: 'Password', type: 'password' },
]
const initialValues: UserFormValues = {
name: '',
email: '',
password: '',
}
function App() {
const handleSubmit = (values: UserFormValues) => {
console.log('Form Submitted', values)
}
return (
<div>
<h1>User Registration</h1>
<GenericForm
fields={userFormFields}
initialValues={initialValues}
onSubmit={handleSubmit}
/>
</div>
)
}
在這個例子中,如果不使用泛型,你需要為每種類型的表單分別創建一個表單組件。使用泛型后,你可以創建一個通用的表單組件,可以用于任何類型的表單字段。這展示了泛型在 React 組件中的強大作用,使得我們的組件更加靈活和可復用。
附加示例:使用泛型創建通用的表格組件
在開發中,表格組件是一個常見的需求。為了使表格組件更加靈活和可重用,我們可以使用 TypeScript 泛型來創建一個通用的表格組件。通過這種方式,我們可以確保數據類型的一致性,并能夠輕松地渲染不同類型的數據。
創建通用表格組件
首先,我們定義一個通用表格組件 Table,它接受一組行數據和一個用于渲染行的函數。
import React from 'react'
const Table = <TRow extends Record<string, any>>(props: {
rows: TRow[];
renderRow: React.FC<TRow>;
}) => {
return (
<table>
<tbody>
{props.rows.map((row, index) => (
<props.renderRow key={index} {...row} />
))}
</tbody>
</table>
)
}
在這個例子中,Table 組件接受一個泛型參數 TRow,它表示表格中每一行的數據類型。組件接收一個 rows 數組和一個 renderRow 函數。renderRow 函數負責渲染每一行的數據。
使用通用表格組件
接下來,我們使用 Table 組件來渲染一個包含姓名和年齡的表格。
import React from 'react'
import { Table } from './Table'
type Person = {
name: string
age: number
}
const people: Person[] = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 40 },
]
const RenderPersonRow: React.FC<Person> = ({ name, age }) => (
<tr>
<td>{name}</td>
<td>{age}</td>
</tr>
)
const App = () => {
return (
<div>
<h1>People Table</h1>
<Table rows={people} renderRow={RenderPersonRow} />
</div>
)
}
export default App
在這個例子中,我們定義了一個 Person 類型,它包含 name 和 age 兩個屬性。然后我們創建了一個 people 數組,包含兩個人的姓名和年齡。RenderPersonRow 是一個用于渲染每行數據的組件,它接受 Person 類型的屬性并返回一個表格行。
我們在 App 組件中使用 Table 組件,將 people 數組作為 rows 傳遞,并將 RenderPersonRow 作為 renderRow 函數傳遞給 Table 組件。這樣,表格組件就會渲染包含兩行數據的表格,每行數據對應一個人的姓名和年齡。
TypeScript 的泛型是一項強大的功能,能夠使你的 React 組件更加靈活和可重用。通過使用泛型,你可以創建適用于任何數據類型的組件,這在處理各種數據類型的實際應用中尤為有用。
如何避免el-form中el-input輸入框回車鍵導致頁面刷新的痛點問題
## 引言:問題起源與影響
在使用Element UI進行Web前端開發時,我們經常遇到一個常見的痛點問題:在`el-form`組件中的`el-input`輸入框內按回車鍵時,頁面會意外地刷新,這并非預期行為,尤其是在表單填寫場景下,用戶希望的是提交表單而非刷新整個頁面。本文將深入探討這個問題,并給出詳盡的解決方案。
### 段落一:問題現象剖析
問題重現
html
<el-form>
<el-form-item label="用戶名">
<el-input v-model="username"></el-input>
</el-form-item>
<!-- 其他表單元素... -->
</el-form>
在上述代碼中,當我們在`el-input`中輸入內容并按下回車鍵時,頁面會發生刷新,這主要是因為瀏覽器默認對`<form>`標簽或可輸入元素(如`<input>`)的回車鍵事件處理為提交表單,而提交操作通常會導致頁面刷新。
### 段落二:解決思路與方法
阻止默認行為
要解決此問題,我們可以采用兩種主要策略:
1.
阻止回車鍵默認提交行為
javascript
export default {
methods: {
preventFormSubmit(e) {
if (e.keyCode === 13) { // keyCode 13代表回車鍵
e.preventDefault(); // 阻止默認提交動作
// 這里可以添加自定義的回車鍵觸發邏輯,例如提交表單
}
}
},
mounted() {
document.addEventListener('keydown', this.preventFormSubmit);
},
beforeDestroy() {
document.removeEventListener('keydown', this.preventFormSubmit);
}
}
2.
局部處理el-input回車事件
html
<el-form @submit.prevent>
<el-form-item label="用戶名">
<el-input v-model="username" @keyup.enter.native.prevent></el-input>
</el-form-item>
<!-- 其他表單元素... -->
</el-form>
在上述代碼中,`.native`修飾符用于監聽原生DOM事件,`@keyup.enter.prevent`表示監聽輸入框的回車鍵按下事件并阻止其默認行為。
自定義回車鍵功能
javascript
// 在main.js或者其他全局引入的文件中
Vue.directive('prevent-form-submit', {
inserted(el, binding, vnode) {
el.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' && e.keyCode === 13) {
e.preventDefault();
const form = vnode.context.$refs[binding.value];
if (form && typeof form.validate === 'function') {
form.validate((valid) => {
if (valid) {
// 觸發表單提交邏輯
form.submit();
}
});
}
}
});
}
});
// 在模板中應用全局指令
<el-form ref="myForm" @submit.prevent v-prevent-form-submit="'myForm'">
<!-- 表單元素... -->
</el-form>
總結起來,解決`el-form`中`el-input`回車鍵導致頁面刷新的問題,關鍵在于理解和利用Vue事件系統以及原生DOM事件的處理機制,通過適當的事件監聽和阻止默認行為,我們可以輕松定制回車鍵的行為,提升用戶體驗,同時避免了不必要的頁面刷新。希望本篇文章能幫助開發者更好地掌握這一技巧,并應用于實際項目中。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。