這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言
我以前很喜歡封裝組件,什么東西不喜歡別人的,總喜歡自己搞搞,這讓人很有成就感,雖然是重復造輪子,但是能從無聊的crud業務中暫時解脫出來,對我來說也算是一種休息,相信有很多人跟我一樣有這個習慣, 這種習慣在獨立開發時無所謂,畢竟沒人會關心你咋實作的,但是在跟人合作時就給別人造成了很大的困擾了,畢竟每個人封裝的東西都是根據自己習慣來的,別人看著多少會有點不順眼,而且自己封裝的組件大概率也是沒有寫檔案和注釋的,所以專案其他成員的使用率也不會太高,所以今天,我試著解決這個問題, 另外,我還在一些群里看到有人抱怨vue3不如vue2好用,主要是適應不了setup寫法,希望這篇博客能改變你的看法,
怎么用hook改造我的組件
關于hook是什么之類的介紹,我這就不贅述了,請看這篇文章淺談:為啥vue和react都選擇了Hooks???, 前言中說到重復造輪子的組件,除開一些毫無必要的重復以外,有一些功能組件確實需要封裝一下,比如說,一些需要請求后端字典到前端展示的下來選擇框,點擊之后要展示loading狀態的按鈕,帶有查詢條件的表單,這些非常常用的業務場景,我們就可以封裝成組件,但是封裝成組件就會遇到前面說的問題,每個人的使用習慣和封裝習慣不一樣,很難讓每個人都滿意,這種場景,就可以讓hook來解決,
普通實作
就拿字典選擇下拉框來說,如果不做封裝,我們是這樣寫的 (這里拿ant-design-vue組件庫來做示例)
<script setup name="DDemo" lang="ts">
import { onMounted, ref } from 'vue';
// 模擬呼叫介面
function getRemoteData() {
return new Promise<any[]>((resolve) => {
setTimeout(() => {
resolve([
{
key: 1,
name: '蘋果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
}, 3000);
});
}
const optionsArr = ref<any[]>([]);
onMounted(() => {
getRemoteData().then((data) => {
optionsArr.value = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/data;
});
});
</script>
看起來很簡單是吧,忽略我們模擬呼叫介面的代碼,我們用在ts/js部分的代碼才只有6行而已,看起來根本不需要什么封裝,
但是這只是一個最簡單的邏輯,不考慮介面請求超時和錯誤的情況,甚至都沒考慮下拉框的loading表現, 如果我們把所有的意外情況都考慮到的話,代碼就會變得很臃腫了,
<script setup name="DDemo" lang="ts">
import { onMounted, ref } from 'vue';
// 模擬呼叫介面
function getRemoteData() {
return new Promise<any[]>((resolve, reject) => {
setTimeout(() => {
// 模擬介面呼叫有概率出錯
if (Math.random() > 0.5) {
resolve([
{
key: 1,
name: '蘋果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
} else {
reject(new Error('不小心出錯了!'));
}
}, 3000);
});
}
const optLoading = ref(false);
const optionsArr = ref<any[]>([]);
function initSelect() {
optLoading.value = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/true;
getRemoteData()
.then((data) => {
optionsArr.value = data;
})
.catch((e) => {
// 請求出線錯誤時將錯誤資訊顯示到select中,給用戶一個友好的提示
optionsArr.value = [
{
key: -1,
value: -1,
label: e.message,
disabled: true,
},
];
})
.finally(() => {
optLoading.value = false;
});
}
onMounted(() => {
initSelect();
});
</script>
這一次,代碼直接來到了22行,雖說用戶體驗確實好了不少,但是這也忒費事了,而且這還只是一個下拉框,頁面里有好幾個下拉框也是很常見的,如此這般,可能什么邏輯都沒寫,頁面代碼就要上百行了,
這個時候,就需要我們來封裝一下了,我們有兩種選擇:
- 把字典下拉框封裝成一個
組件;
- 把請求、加載中、錯誤這些處理邏輯封裝到
hook里;
第一種大家都知道,就不多說了,直接說第二種
封裝下拉框hook
import { onMounted, reactive, ref } from 'vue';
// 定義下拉框接收的資料格式
export interface SelectOption {
value: string;
label: string;
disabled?: boolean;
key?: string;
}
// 定義入參格式
interface FetchSelectProps {
apiFun: () => Promise<any[]>;
}
export function useFetchSelect(props: FetchSelectProps) {
const { apiFun } = props;
const options = ref<SelectOption[]>([]);
const loading = ref(false);
/* 呼叫介面請求資料 */
const loadData = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/() => {
loading.value = true;
options.value = [];
return apiFun().then(
(data) => {
loading.value = false;
options.value = data;
return data;
},
(err) => {
// 未知錯誤,可能是代碼拋出的錯誤,或是網路錯誤
loading.value = false;
options.value = [
{
value:'-1',
label: err.message,
disabled: true,
},
];
// 接著拋出錯誤
return Promise.reject(err);
}
);
};
// onMounted 中呼叫介面
onMounted(() => {
loadData();
});
return reactive({
options,
loading,
});
}
然后在組件中調用
<script setup name="DDemo" lang="ts">
import { useFetchSelect } from './hook';
// 模擬呼叫介面
function getRemoteData() {
return new Promise<any[]>((resolve, reject) => {
setTimeout(() => {
// 模擬介面呼叫有概率出錯
if (Math.random() > 0.5) {
resolve([
{
key: 1,
name: '蘋果',
value: 1,
},
{
key: 2,
name: '香蕉',
value: 2,
},
{
key: 3,
name: '橘子',
value: 3,
},
]);
} else {
reject(new Error('不小心出錯了!'));
}
}, 3000);
});
}
// 將之前用的 options,loading,和呼叫介面的邏輯都抽離到hook中
const selectBind = useFetchSelect({
apiFun: getRemoteData,
});
</script>
<template>
<div>
<!-- 將hook回傳的介面,通過 v-bind 系結給組件 -->
<a-select v-bind="selectBind" />
</div>
</template>
這樣一來,代碼行數直接又從20行降到3行,甚至比剛開始最簡單的那個還要少兩行,但是功能卻一點不少,用戶體驗也是比較完善的,
如果你覺著上面這個例子不能打動你的話,可以看看下面這個
Loading狀態hook
點擊按鈕,呼叫介面是另一個我們經常遇到的場景,為了更好的用戶體驗,提示用戶操作已經回應,同時防止用戶多次點擊,我們要在呼叫介面的同時將按鈕置為loading狀態,雖說只有一個loading狀態,但是寫多了也覺著麻煩,
為此我們可以封裝一個非常簡單的hook:
hook.ts
import { Ref, ref } from 'vue';
type TApiFun<TData, TParams extends Array<any>> = (...params: TParams) => Promise<TData>;
interface AutoRequestOptions {
// 定義一下初始狀態
loading?: boolean;
// 介面呼叫成功時的回呼
onSuccess?: (data: any) => void;
}
type AutoRequestResult<TData, TParams extends Array<any>> = [Ref<boolean>, TApiFun<TData, TParams>];
/* 控制loading狀態的自動切換hook */
export function useAutoRequest<TData, TParams extends any[] = any[]>(fun: TApiFun<TData, TParams>, options?: AutoRequestOptions): AutoRequestResult<TData, TParams> {
const { loading = false, onSuccess } = options || { loading: false };
const requestLoading = ref(loading);
const run: TApiFun<TData, TParams> = (...params) => {
requestLoading.value = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/true;
return fun(...params)
.then((res) => {
onSuccess && onSuccess(res);
return res;
})
.finally(() => {
requestLoading.value = false;
});
};
return [requestLoading, run];
}
這次把模擬介面的方法單獨抽出一個檔案
api/index.ts
export function submitApi(text: string) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 模擬介面呼叫有概率出錯
if (Math.random() > 0.5) {
resolve({
status: "ok",
text: text,
});
} else {
reject(new Error("不小心出錯了!"));
}
}, 3000);
});
}
使用:
index.vue
<script setup name="Index" lang="ts">
import { useAutoRequest } from "./hook";
import { Button } from "ant-design-vue";
import { submitApi } from "@/api";
const [loading, submit] = useAutoRequest(submitApi);
function onSubmit() {
submit("aaa").then((res) => {
console.log("res", res);
});
}
</script>
<template>
<div >
<Button :loading="loading" @click="onSubmit">提交</Button>
</div>
</template>
這樣封裝一下,我們使用時就不再需要手動切換loading的狀態了,
這個hook還有另一種玩法:
hook2.ts
import type { Ref } from "vue";
import { ref } from "vue";
type AutoLoadingResult = [
Ref<boolean>,
<T>(requestPromise: Promise<T>) => Promise<T>
];
/* 在給run方法傳入一個promise,會在promise執行前或執行后將loading狀態設為true,在執行完成后設為false */
export function useAutoLoading(defaultLoading = false): AutoLoadingResult {
const ld = ref(defaultLoading);
function run<T>(requestPromise: Promise<T>): Promise<T> {
ld.value = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/true;
return requestPromise.finally(() => {
ld.value = false;
});
}
return [ld, run];
}
使用:
index.vue
<script setup name="Index" lang="ts">
// import { useAutoRequest } from "./hook";
import { useAutoLoading } from "./hook2";
import { Button } from "ant-design-vue";
import { submitApi, cancelApi } from "@/api";
// const [loading, submit] = useAutoRequest(submitApi);
const [commonLoading, fetch] = useAutoLoading();
function onSubmit() {
fetch(submitApi("submit")).then((res) => {
console.log("res", res);
});
}
function onCancel() {
fetch(cancelApi("cancel")).then((res) => {
console.log("res", res);
});
}
</script>
<template>
<div >
<Button type="primary" :loading="commonLoading" @click="onSubmit">
提交
</Button>
<Button :loading="commonLoading" @click="onCancel">取消</Button>
</div>
</template>
這里也是用到了promise鏈式呼叫的特性,在接口呼叫之后馬上將loading置為true,在介面呼叫完成后置為false,而useAutoRequest則是在介面呼叫之前就將loading置為true,
useAutoRequest呼叫時代碼更簡潔,useAutoLoading的使用則更靈活,可以同時服務給多個介面使用,比較適合提交、取消這種互斥的場景,
解放組件
如果你翻看過我的這篇博客一個省心省力的骨架屏實作方案,那么肯定知道在骨架屏組件中,我是用了傳入的res物件的code屬性來判斷當前顯示的視圖狀態,長話短說就是, res是介面回傳給前端的資料,如
{
"code":0,
"msg":'查詢成功',
"data":{
"username":"小王",
"age":20,
}
}
我們假定當code為0時代表成功,不為0表示失敗,為-100時表示正在加載,當然介面并不會也不需要回傳-100,-100是我們本地捏造出來的,只是為了讓骨架屏組件顯示對應的加載狀態, 在頁面中使用時,我們需要先宣告一個code為-100的res物件系結給骨架屏組件,然后在onMounted中呼叫查詢介面,呼叫成功后更新res物件,
如果像上面這樣使用res物件來給骨架屏組件設定狀態的話,就感覺非常的麻煩,有時候我們只是要設定一個初始時的加載狀態,但是要搞好幾行沒用的代碼,但是如果我們把res拆解成一個個引數單獨傳遞的話,父組件需要維護的變數就會非常多了,這時我們就可以封裝hook來解決這個問題,把拆解出來的引數都扔到hook里面保存,
上代碼(這部分代碼比較長,想要詳細了解的話可以去看原文章)
骨架屏組件
SkeletonView/index.vue
<script setup lang="ts">
import { defineProps, computed } from "vue";
import { LoadingOutlined } from "@ant-design/icons-vue";
import { isArray } from "@/utils/is";
import { Button } from "ant-design-vue";
/* status:'loading','error','success','empty' */
type ViewStatus = "loading" | "error" | "success" | "empty";
interface SkeletonProps<T = any> {
status: ViewStatus;
result: T;
placeholderResult: T;
emptyMsg?: string;
errorMsg?: string;
isEmpty?: (result: T) => boolean;
}
const props = withDefaults(defineProps<SkeletonProps>(), {
status: "loading",
emptyMsg: "暫無資料",
errorMsg: "未知錯誤",
});
const emits = defineEmits(["retry"]);
const retryClick = () => {
emits("retry");
};
const viewStatus = computed(() => {
const status = props.status;
if (status === "success") {
let isEmp = false;
const result = props.result;
if (props.isEmpty) {
isEmp = props.isEmpty(props.result);
} else {
if (isArray(result)) {
isEmp = result.length === 0;
} else if (!result) {
isEmp = true;
} else {
isEmp = false;
}
}
if (isEmp) {
return "empty";
}
return "success";
}
return status;
});
const placeholderData = https://www.cnblogs.com/smileZAZ/archive/2023/02/08/computed(() => {
if (props.result) {
return props.result;
}
return props.placeholderResult;
});
</script>
使用 index.vue
<script setup name="SkeletonView" lang="ts">
import SkeletonView from "@/components/SkeletonView/index.vue";
import { useAutoSkeletonView } from "./useAutoSkeletonView";
import { listApi } from "@/api";
const view = useAutoSkeletonView({
apiFun: listApi,
});
</script>
<template>
<div >
<SkeletonView
v-slot="{ result }"
v-bind="view.bindProps"
v-on="view.bindEvents"
>
<span>{{ result }}</span>
</SkeletonView>
</div>
</template>
這里的SkeletonView不光用v-bind系結了hook拋出的屬性,還用v-on系結的事件,目的就是監聽請求報錯時出現的“重試”按鈕的點擊事件,
使用優化
經常寫react的朋友可能早就看出來了,這不是跟react中的一部分hook用法如出一轍嗎?沒錯,很多人寫react就這么寫,而且react中系結hook跟組件更簡單,只需要...就可以了,比如:
function Demo(){
const select = useSelect({
apiFun:getDict
})
// 這里可以直接用...將useSelect回傳的屬性與方法全部系結給Select組件
return <Select {...select}>;
}
比起vue的v-bind和v-on算是簡便了不少,那么,有沒有一種辦法也能做到差不多的效果呢?就比如能做到v-xxx="select",
博主首先想到的就是vue的自定義指令了,檔案在這里,但是折騰了半天發現行不通,因為自定義指令主要還是針對dom來的,vue官網原話:
總的來說,不推薦在組件上使用自定義指令,
那么就只能考慮打包插件了,只要我們在vue決議template之前把v-xxx="select"翻譯成v-bind="select.bindProps" v-on="select.bindEvents" 就好了,聽起來并不難,只要我們開發的時候規定系結組件的hook回傳格式必須有bindProps和bindEvents就好了,
思路有了,直接開干,現在vue官網的默認創建方式也改成vite,我們就直接寫vite的插件(不想看可以跳到最后用現成的):
// component-enhance-hook
import type { PluginOption } from "vite";
// 可以自定義hook系結的前綴、系結的屬性值合集對應的鍵和事件合集對應的鍵
type HookBindPluginOptions = {
prefix?: string;
bindKey?: string;
eventKey?: string;
};
export const viteHookBind = (options?: HookBindPluginOptions): PluginOption => {
const { prefix, bindKey, eventKey } = Object.assign(
{
prefix: "v-ehb",
bindKey: "bindProps",
eventKey: "bindEvents",
},
options
);
return {
name: "vite-plugin-vue-component-enhance-hook-bind",
enforce: "pre",
transform: (code, id) => {
const last = id.substring(id.length - 4);
if (last === ".vue") {
// 處理之前先判斷一下
if (code.indexOf(prefix) === -1) {
return code;
}
// 獲取 template 開頭
const templateStrStart = code.indexOf("<template>");
// 獲取 template 結尾
const templateStrEnd = code.lastIndexOf("</template>");
let templateStr = code.substring(templateStrStart, templateStrEnd + 11);
let startIndex;
// 回圈轉換 template 中的hook系結指令
while ((startIndex = templateStr.indexOf(prefix)) > -1) {
const endIndex = templateStr.indexOf(`"`, startIndex + 7);
const str = templateStr.substring(startIndex, endIndex + 1);
const obj = str.split(`"`)[1];
const newStr = templateStr.replace(
str,
`v-bind="${obj}.${bindKey}" v-on="${obj}.${eventKey}"`
);
templateStr = newStr;
}
// 拼接并回傳
return (
code.substring(0, templateStrStart) +
templateStr +
code.substring(templateStrEnd + 11)
);
}
return code;
},
};
};
應用插件
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { viteHookBind } from "./vBindPlugin";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx(), viteHookBind()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
修改一下vue中的用法
<script setup name="SkeletonView" lang="ts">
import SkeletonView from "@/components/SkeletonView/index.vue";
import { useAutoSkeletonView } from "./useAutoSkeletonView";
import { listApi } from "@/api";
const view = useAutoSkeletonView({
queryInMount: true,
apiFun: listApi,
placeholderResult: [
{
key: 1,
name: "蘋果",
value: 1,
},
{
key: 2,
name: "香蕉",
value: 2,
},
{
key: 3,
name: "橘子",
value: 3,
},
],
});
</script>
<template>
<div >
<SkeletonView v-slot="{ result }" v-ehb="view">
<span>{{ result }}</span>
</SkeletonView>
</div>
</template>
OK! 完成了!
使用npm安裝
不過我也提前打包編譯好了發布在了npm上,需要的話可以直接使用這個
npm i vite-plugin-vue-hook-enhance -D
改一下引入方式就可以了
import { viteHookBind } from "vite-plugin-vue-hook-enhance";
本文轉載于:
https://juejin.cn/post/7181712900094951483
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/543326.html
標籤:其他
上一篇:react腳手架配置代理總結
下一篇:PC移動端面試題
-
- 標籤雲
-
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)
-
- 熱門瀏覽
-
-
IEEE1588PTP在數字化變電站時鐘同步方面的應用
IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......
uj5u.com 2020-09-10 03:51:52 more
-
HTTP request smuggling CL.TE
CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......
uj5u.com 2020-09-10 03:52:11 more
-
網路滲透資料大全單——漏洞庫篇
網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......
uj5u.com 2020-09-10 03:52:15 more
-
京準講述NTP時鐘服務器應用及原理
京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......
uj5u.com 2020-09-10 03:52:25 more
-
利用北斗衛星系統設計NTP網路時間服務器
利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......
uj5u.com 2020-09-10 03:52:35 more
-
詳細解讀電力系統各種對時方式
詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......
uj5u.com 2020-09-10 03:52:45 more
-
如何保證外包團隊接入企業內網安全
不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......
uj5u.com 2020-09-10 03:52:57 more
-
PHP漏洞之【整型數字型SQL注入】
0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......
uj5u.com 2020-09-10 03:55:40 more
-
[GXYCTF2019]禁止套娃
git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......
uj5u.com 2020-09-10 03:56:07 more
-
-
- 最新发布
-
-
使用Django Rest framework搭建Blog
在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......
uj5u.com 2023-04-20 08:17:54 more
-
記錄-new Date() 我忍你很久了!
這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......
uj5u.com 2023-04-20 08:17:47 more
-
使用Vue.js實作文字跑馬燈效果
實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......
uj5u.com 2023-04-20 08:12:31 more
-
JavaScript 運算子
JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......
uj5u.com 2023-04-20 08:02:25 more
-
CSS—相對單位rem
一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......
uj5u.com 2023-04-20 08:02:21 more
-
我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明
好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......
uj5u.com 2023-04-20 08:01:50 more
-
如何在 vue3 中使用 jsx/tsx?
我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......
uj5u.com 2023-04-20 08:01:37 more
-
【Vue2.x原始碼系列06】計算屬性computed原理
本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......
uj5u.com 2023-04-20 08:01:31 more
-
http1.1與http2.0
一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......
uj5u.com 2023-04-20 08:01:10 more
-
http1.1與http2.0
一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......
uj5u.com 2023-04-20 08:00:32 more
- 友情鏈接
-
-
