使用Vue3實(shí)現(xiàn)一個(gè)Upload組件的示例代碼
通用上傳組件開(kāi)發(fā)
開(kāi)發(fā)上傳組件前我們需要了解:
- FormData上傳文件所需API
- dragOver文件拖拽到區(qū)域時(shí)觸發(fā)
- dragLeave 文件離開(kāi)拖動(dòng)區(qū)域
- drop文件移動(dòng)到有效目標(biāo)時(shí)
首先實(shí)現(xiàn)一個(gè)最基本的上傳流程:
基本上傳流程,點(diǎn)擊按鈕選擇,完成上傳
代碼如下:
<template> <div class="app-container"> <!--使用change事件--> <input type="file" @change="handleFileChange"> </div> </template> <script lang="ts"> import { defineComponent } from 'vue' import axios from 'axios' export default defineComponent({ name: 'App', setup() { const handleFileChange = (e: Event) => { // 斷言為HTMLInputElement const target = e.target as HTMLInputElement const files = target.files if(files) { const uploadedFile = files[0] const formData = new FormData() formData.append('file', uploadedFile) // 使用node模擬上傳接口 axios.post('http://localhost:3001/upload', formData, { headers: { "Content-Type": 'multipart/form-data' } }).then(resp=> { console.log('resp', resp) }).catch(error=> { }) } } return { handleFileChange } } }) </script> <style> .page-title { color: #fff; } </style>
結(jié)果如下:
到這里我們基本的上傳已經(jīng)處理完成了,相對(duì)來(lái)說(shuō)還是比較簡(jiǎn)單的,接下來(lái)我們創(chuàng)建Uploader.vue文件用來(lái)封裝Upload組件。
我們需要實(shí)現(xiàn)如下功能
自定義模版
我們知道使用<input type="file">系統(tǒng)自帶樣式比較難看所用我們需要如下處理:
- 對(duì)樣式進(jìn)行優(yōu)化,隱藏input
- 點(diǎn)擊<div class="upload-area" @click="triggerUpload"></div>使用js出發(fā)input的click事件
- 處理上傳狀態(tài)
代碼如下:
<template> <div class="file-upload"> <div class="upload-area" @click="triggerUpload"></div> <span v-if="fileStatus==='loading'">正在上傳</span> <span v-else-if="fileStatus==='success'">上傳成功</span> <span v-else-if="fileStatus==='error'">上傳失敗</span> <span v-else>點(diǎn)擊上傳</span> <input ref="fileInput" type="file" name="file" style="display: none" /> </div> </template> <script lang="ts"> import { computed, defineComponent, PropType, reactive, ref } from 'vue' import axios from 'axios' type UploadStatus = 'ready' | 'loading' | 'success' | 'error' export default defineComponent({ props: { action: { // url地址 type: String, required: true } }, setup(props) { // input實(shí)例對(duì)象,通過(guò)與ref="fileInput"的關(guān)聯(lián)獲取到input實(shí)例對(duì)象 const fileInput = ref<null | HTMLInputElement>(null) const fileStatus = ref<UploadStatus>('ready') // 1.div點(diǎn)擊事件 const triggerUpload = () => { if(fileInput.value) { fileInput.value.click() } } // 通過(guò)div來(lái)觸發(fā)input的change事件 const handleFileChange = (e: Event) => { const target = e.target as HTMLInputElement const files = target.files if(files) { const uploadedFile = files[0] const formData = new FormData() formData.append('file', uploadedFile) readyFile.status = 'loading' // 上傳之前將狀態(tài)設(shè)置為loading axios.post(props.actions, formData, { headers: { "Content-Type": 'multipart/form-data' } }).then(resp=> { console.log('resp', resp) readyFile.status = 'success' // 上傳成功將狀態(tài)設(shè)置為success }).catch(error=> { readyFile.status = 'error' // // 上傳失敗將狀態(tài)設(shè)置為error }) } } return { fileInput, triggerUpload, handleFileChange, fileStatus } } }) </script>
現(xiàn)在我們已經(jīng)完成了對(duì)上傳組件樣式的優(yōu)化,和上傳狀態(tài)的處理,接下來(lái)我們需要處理文件上傳列表
支持文件上傳列表
處理文件上傳列表我們需要有如下實(shí)現(xiàn):
- 顯示文件名稱(chēng)
- 狀態(tài)
- 可刪除
- 顯示上傳進(jìn)度
- 有可能有更豐富的顯示
在上一步代碼的基礎(chǔ)上做一些修改:
<template> <div class="file-upload"> <div class="upload-area" @click="triggerUpload"></div> <!-- 點(diǎn)擊上傳態(tài) --> <slot v-if="isUploading" name='loading'> <button disabled>正在上傳</button> </slot> <!-- 上傳完畢態(tài) --> <slot name="uploaded" v-else-if="lastFileData && lastFileData.loaded" :uploadData="lastFileData.data"> <button disabled>點(diǎn)擊上傳</button> </slot> <!-- 默認(rèn)態(tài) --> <slot v-else name='default'> <!-- <button disabled>點(diǎn)擊上傳</button> --> <span>點(diǎn)擊上傳</span> </slot> <input ref="fileInput" type="file" name="file" style="display: none" /> <!--展示fileList--> <ul> <li v-for="file in filesList" :key="file.uid" :class="`uploaded-file upload-${file.status}`" > <span class="filname"> {{ file.name }} </span> <button class="delete-icon" @click="reomveFile(file.uid)">Del</button> </li> </ul> </div> </template> <script lang="ts"> import { last } from 'lodash-es' import { v4 as uuidv4 } from 'uuid'; // 定義上傳狀態(tài) type UploadStatus = 'ready' | 'loading' | 'success' | 'error' // step1 定義上傳文件對(duì)象接口類(lèi) export interface UploadFile { uid: string; // 文件唯一id size: number; // 文件大小 name: string; // 文件名稱(chēng) status: UploadStatus; // 上傳狀態(tài) raw: File; // 文件 progress?: string; // 文件上傳進(jìn)度 resp?: any; // 服務(wù)端返回?cái)?shù)據(jù) url?: string // 對(duì)應(yīng)展示的url } export default defineComponent({ props: { action: { // url地址 type: String, required: true } }, setup(props) { // input實(shí)例對(duì)象,通過(guò)與ref="fileInput"的關(guān)聯(lián)獲取到input實(shí)例對(duì)象 const fileInput = ref<null | HTMLInputElement>(null) // step2 上傳文件列表 const filesList = ref<UploadFile[]>([]) // step4-1 判斷是否正在上傳中 const isUploading = computed(()=> { return filesList.value.some((file)=>file.status==='loading') }) //step4-2 獲取上傳文件的最后一項(xiàng) const lastFileData = computed(()=>{ const lastFile = last(filesList.value) if(!lastFile) return false return { loaded: lastFile?.status === 'success', data: lastFile?.resp } }) // 1.div點(diǎn)擊事件 const triggerUpload = () => { if(fileInput.value) { fileInput.value.click() } } // 通過(guò)div來(lái)觸發(fā)input的change事件 const handleFileChange = (e: Event) => { const target = e.target as HTMLInputElement const files = target.files if(files) { const uploadedFile = files[0] const formData = new FormData() formData.append('file', uploadedFile) // step3 設(shè)置響應(yīng)式對(duì)象,存儲(chǔ)到filesList中,以便在頁(yè)面中展示 const fileObj = reactive<UploadFile>({ uid: uuid(); // 文件唯一id size: uploadedFile.size, name: uploadedFile.name, status: 'loading', raw: uploadedFile }) filesList.value.push(fileObj) axios.post(props.actions, formData, { headers: { "Content-Type": 'multipart/form-data' }, //step6 處理上傳進(jìn)度 onUploadProgress: (progressEvent)=> { const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%' fileObj.progress = complete } }).then(resp=> { console.log('resp', resp) fileObj.status = 'success' }).catch(error=> { fileObj.status = 'error' }).finally(()=> { // 一張圖片重復(fù)上傳時(shí)無(wú)法繼續(xù)上傳bug if(fileInput.value) { fileInput.value.value = '' } }) } } // step7 處理刪除 const reomveFile = (uid: string) => { filesList.value = filesList.value.filter(file=>file.uid!==uid) } return { fileInput, triggerUpload, handleFileChange, fileStatus, isUploading, filesList, lastFileData } } }) </script>
- 首先我們定義上傳文件對(duì)象接口類(lèi)UploadFile
- 創(chuàng)建了一個(gè)filesList響應(yīng)式對(duì)象
- 去掉了fileStatus,因?yàn)樵赨ploadFile接口中我們已經(jīng)有定義status
- 修改模版中的狀態(tài),利用computed來(lái)判讀是否是處于上傳狀態(tài),并增加slot進(jìn)行自定義
- 展示上傳的圖片
- 處理上傳進(jìn)度
- 處理刪除
注意點(diǎn):如果選擇相同的圖片不會(huì)進(jìn)行上傳操作,因?yàn)槲覀兪褂玫氖莍nput的change事件,所以我們需要在上傳后將input的value設(shè)置為null
支持一系列生命周期鉤子事件,上傳事件
beforeUpload
<template> ... </template> <script lang="ts"> import { last } from 'lodash-es' import { v4 as uuidv4 } from 'uuid'; // 定義上傳狀態(tài) type UploadStatus = 'ready' | 'loading' | 'success' | 'error' // 定義上傳文件為boolean或promsie且接受一個(gè)File type CheckUpload = ()=> boolean | Promise<File> export interface UploadFile { ... } export default defineComponent({ props: { action: { // url地址 type: String, required: true }, beforeUpload: { type: Function as PropType<CheckUpload> } }, setup(props) { const fileInput = ref<null | HTMLInputElement>(null) const filesList = ref<UploadFile[]>([]) // step4-1 判斷是否正在上傳中 const isUploading = computed(()=> { return filesList.value.some((file)=>file.status==='loading') }) const lastFileData = computed(()=>{ const lastFile = last(filesList.value) if(!lastFile) return false return { loaded: lastFile?.status === 'success', data: lastFile?.resp } }) const triggerUpload = () => { if(fileInput.value) { fileInput.value.click() } } const handleFileChange = (e: Event) => { const target = e.target as HTMLInputElement const files = target.files const uploadedFile = files[0] if(props.beforeUpload) { const result = props.beforeUpload(uploadedFile) if(result && result instanceof Promise) { result.then((processFile)=> { if(uploadedFile instanceof File) { postFile(uploadedFile) } }).catch(error=>{ console.error(error) }) } esle if(result===true) { postFile(uploadedFile) } }else{ postFile(uploadedFile) } } const reomveFile = (uid: string) => { filesList.value = filesList.value.filter(file=>file.uid!==uid) } // 處理文件上傳 const postFile = (readyFile:UploadFile)=> { const formData = new FormData() formData.append('file', readyFile.raw) readyFile.status = 'loading' axios.post(props.action, formData, { headers: { "Content-Type": 'multipart/form-data' }, onUploadProgress: (progressEvent)=> { const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%' // console.log('上傳 ' + complete) readyFile.progress = complete } }, ).then(resp=> { console.log('resp', resp) // fileStatus.value = 'success' readyFile.status = 'success' readyFile.resp = resp.data }).catch(error=> { // fileStatus.value = 'error' readyFile.status = 'error' }) .finally(()=> { // 一張圖片重復(fù)上傳時(shí)無(wú)法繼續(xù)上傳bug if(fileInput.value) { fileInput.value.value = '' } }) } return { fileInput, triggerUpload, handleFileChange, fileStatus, isUploading, filesList, lastFileData } } }) </script>
實(shí)現(xiàn)步驟:
- 在poops處定義屬性beforeUpload,同時(shí)定義上傳文件為boolean或promsie且接受一個(gè)File
- 將原上傳方法進(jìn)行封裝為postFile
- 根據(jù)beforeUpload返回結(jié)果,進(jìn)行接下來(lái)的流程處理
onProgress,onSuccess,onError,onChange與之類(lèi)似
拖拽支持
大致流程如下圖:
- dragover和dragleave添加和刪除對(duì)應(yīng)的class
- drop拿到正在被拖拽的文件,刪除class,并且觸發(fā)上傳
- 只有在屬性drag為true才能觸發(fā)
需要注意v-on的使用具體可參考vue官方文檔
上代碼:
<template> <div class="file-upload"> <div :class="['upload-area', drag && isDragOver ? 'is-dragover': '' ]" v-on="events" > <!-- 點(diǎn)擊上傳態(tài) --> <slot v-if="isUploading" name='loading'> <button disabled>正在上傳</button> </slot> <!-- 上傳完畢態(tài) --> <slot name="uploaded" v-else-if="lastFileData && lastFileData.loaded" :uploadData="lastFileData.data"> <button disabled>點(diǎn)擊上傳</button> </slot> <!-- 默認(rèn)態(tài) --> <slot v-else name='default'> <!-- <button disabled>點(diǎn)擊上傳</button> --> <span>點(diǎn)擊上傳</span> </slot> </div> <input ref="fileInput" type="file" name="file" style="display: none" @change="handleFileChange" /> <ul> <li v-for="file in filesList" :key="file.uid" :class="`uploaded-file upload-${file.status}`" > <img v-if="file.url && listType === 'picture'" class="upload-list-thumbnail" :src="file.url" :alt="file.name" > <span class="filname"> {{ file.name }} </span> <span class="progress"> {{ file.progress }} </span> <button class="delete-icon" @click="reomveFile(file.uid)">Del</button> </li> </ul> </div> </template> <script lang="ts"> import { computed, defineComponent, PropType, reactive, ref } from 'vue' import axios from 'axios' import { v4 as uuidv4 } from 'uuid'; import { last } from 'lodash-es' type UploadStatus = 'ready' | 'loading' | 'success' | 'error' type fileListType = 'text' | 'picture' type CheckUpload = ()=> boolean | Promise<File> export interface UploadFile { uid: string; // 文件唯一id size: number; // 文件大小 name: string; // 文件名稱(chēng) status: UploadStatus; // 上傳狀態(tài) raw: File; // 文件 progress?: string; resp?: any; // 服務(wù)端返回?cái)?shù)據(jù) url?: string // 對(duì)應(yīng)展示的url } // dragOver 文件拖拽到區(qū)域時(shí)觸發(fā) // dragLeave 文件離開(kāi)拖動(dòng)區(qū)域 // drop 事件拿到正在被拖拽的文件刪除class并且觸發(fā)上傳 // 只有設(shè)置drag的時(shí)候才有效 export default defineComponent({ name: 'Uploader', props: { action: { // url地址 type: String, required: true }, beofreUpload:{ // 上傳之前的處理 type: Function as PropType<CheckUpload> }, drag: { // 是否拖拽 type: Boolean, default: false }, autoUpload: { // 自動(dòng)上傳 type: Boolean, default: true }, listType: { type: String as PropType<fileListType>, default: 'text' } }, setup(props) { const fileInput = ref<null | HTMLInputElement>(null) const fileStatus = ref<UploadStatus>('ready') // 存儲(chǔ)上傳的文件 const filesList = ref<UploadFile[]>([]) // 定義了一個(gè)isDragOver標(biāo)識(shí),處理拖動(dòng)后樣式.upload-area顯示 const isDragOver = ref<boolean>(false) const triggerUpload = () => { if(fileInput.value) { fileInput.value.click() } } let events: {[key:string]: (e: any)=>void} = { 'click': triggerUpload, } // 只要有一個(gè)文件狀態(tài)是loading態(tài)就說(shuō)明是正在上傳 const isUploading = computed(()=>{ return filesList.value.some((file)=> file.status==='loading') }) // 獲取上傳文件的最后一項(xiàng) const lastFileData = computed(()=>{ const lastFile = last(filesList.value) if(!lastFile) return false return { loaded: lastFile?.status === 'success', data: lastFile?.resp } }) // 處理dragover,dragleave const handleDrag = (e: DragEvent,over: boolean)=> { // 阻止默認(rèn)事件 e.preventDefault() // dragover為true,dragleave為fasle isDragOver.value = over } // 處理drop const handleDrop = (e: DragEvent) => { e.preventDefault() isDragOver.value = false if(e.dataTransfer) { beforeUploadCheck(e.dataTransfer.files) } } if(props.drag){ events = { ...events, 'dragover': (e: DragEvent) => { handleDrag(e, true)}, 'dragleave': (e: DragEvent) => { handleDrag(e, false)}, 'drop': handleDrop } // console.log(events) } // 刪除文件 const reomveFile = (uid: string)=> { filesList.value = filesList.value.filter(file=>file.uid!==uid) } const postFile = (readyFile:UploadFile) => { const formData = new FormData() formData.append('file', readyFile.raw) readyFile.status = 'loading' // 選擇文件后push到存儲(chǔ)對(duì)象里面 axios.post(props.action, formData, { headers: { "Content-Type": 'multipart/form-data' }, onUploadProgress: (progressEvent)=> { const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%' // console.log('上傳 ' + complete) readyFile.progress = complete } }, ).then(resp=> { console.log('resp', resp) // fileStatus.value = 'success' readyFile.status = 'success' readyFile.resp = resp.data }).catch(error=> { // fileStatus.value = 'error' readyFile.status = 'error' }) .finally(()=> { // 一張圖片重復(fù)上傳時(shí)無(wú)法繼續(xù)上傳bug if(fileInput.value) { fileInput.value.value = '' } }) } const addFileToList = (uploadedFile: File) => { const fileObj = reactive<UploadFile>({ uid: uuidv4(), size: uploadedFile.size, name: uploadedFile.name, status: 'ready', raw: uploadedFile }) // 處理圖片格式并顯示 if(props.listType==='picture') { // try { // fileObj.url = URL.createObjectURL(uploadedFile) // }catch(err) { // console.error('upload file err', err) // } const fileReader = new FileReader() fileReader.readAsDataURL(uploadedFile) fileReader.addEventListener('load', ()=> { fileObj.url = fileReader.result as string }) } filesList.value.push(fileObj) if(props.autoUpload) { postFile(fileObj) } } const uploadFiles = ()=> { // filesList.value.filter(file => file.status === 'ready').forEach(readyFile => postFile(readyFile)) filesList.value.filter(file => file.status === 'ready').forEach(readyFile=>postFile(readyFile)) } const beforeUploadCheck = (files: null | FileList ) => { if(files) { fileStatus.value = 'loading' const uploadedFile = files[0] if(props.beofreUpload) { const result = props.beofreUpload() // 如果有返回值 if(result && result instanceof Promise) { result.then(processedFile=> { if(processedFile instanceof File) { addFileToList(processedFile) } else { throw new Error('beforeUpload promise should return file object') } }).catch(err=> { console.log(err) }) } else if(result === true) { addFileToList(uploadedFile) } } else { addFileToList(uploadedFile) } } } const handleFileChange = (e: Event) => { const target = e.target as HTMLInputElement const files = target.files beforeUploadCheck(files) } return { fileInput, triggerUpload, handleFileChange, isUploading, filesList, reomveFile, lastFileData, beforeUploadCheck, isDragOver, events } } }) </script>
以上就是基本的基于vue3實(shí)現(xiàn)的上傳通用組件
另附上上傳接口代碼:
// 引入模塊 const Koa = require('koa'); const fs = require('fs'); const path = require('path'); const router = require('koa-router')(); const koaBody = require('koa-body'); const static = require('koa-static'); const cors = require('koa2-cors') // 實(shí)例化 const app = new Koa(); app.use(koaBody({ multipart: true, // 支持文件上傳 formidable: { maxFieldsSize: 2 * 1024 * 1024, // 最大文件為2兆 multipart: true // 是否支持 multipart-formdate 的表單 } })); const uploadUrl = "http://localhost:3001/static/upload"; // 配置路由 router.get('/', (ctx) => { // 設(shè)置頭類(lèi)型, 如果不設(shè)置,會(huì)直接下載該頁(yè)面 ctx.type = 'html'; // 讀取文件 const pathUrl = path.join(__dirname, '/static/upload.html'); ctx.body = fs.createReadStream(pathUrl); }); // 上傳文件 router.post('/upload', (ctx) => { // 獲取上傳文件 const file = ctx.request.files.file; console.log(file); // 讀取文件流 const fileReader = fs.createReadStream(file.path); // 設(shè)置文件保存路徑 const filePath = path.join(__dirname, '/static/upload/'); // 組裝成絕對(duì)路徑 const fileResource = filePath + `/${file.name}`; /** * 使用 createWriteStream 寫(xiě)入數(shù)據(jù),然后使用管道流pipe拼接 */ const writeStream = fs.createWriteStream(fileResource); // 判斷 /static/upload 文件夾是否存在,如果不在的話就創(chuàng)建一個(gè) if (!fs.existsSync(filePath)) { fs.mkdir(filePath, (err) => { if (err) { throw new Error(err); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上傳成功1' }; } }); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上傳成功1' }; } }); // 配置靜態(tài)資源路徑 app.use(static(path.join(__dirname))); // 開(kāi)啟跨域 app.use(cors()) // 啟動(dòng)路由 app.use(router.routes()).use(router.allowedMethods()); // 監(jiān)聽(tīng)端口號(hào) app.listen(3001, () => { console.log('server is listen in 3001'); });
寫(xiě)在最后
已上只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),當(dāng)然了我們也可以添加自定義headers,自定義傳遞數(shù)據(jù)data,accecpt等等
到此這篇關(guān)于使用Vue3實(shí)現(xiàn)一個(gè)Upload組件的示例代碼的文章就介紹到這了,更多相關(guān)Vue3 Upload組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue中element 的upload組件發(fā)送請(qǐng)求給后端操作
- vue element upload組件 file-list的動(dòng)態(tài)綁定實(shí)現(xiàn)
- vue-cli3.0+element-ui上傳組件el-upload的使用
- 基于vue-upload-component封裝一個(gè)圖片上傳組件的示例
- vue2.0 使用element-ui里的upload組件實(shí)現(xiàn)圖片預(yù)覽效果方法
- 在vue項(xiàng)目中使用element-ui的Upload上傳組件的示例
- vue webuploader 文件上傳組件開(kāi)發(fā)
- Vue上傳組件vue Simple Uploader的用法示例
相關(guān)文章
移動(dòng)端H5開(kāi)發(fā) Turn.js實(shí)現(xiàn)很棒的翻書(shū)效果
這篇文章主要為大家詳細(xì)介紹了Turn.js實(shí)現(xiàn)很棒的翻書(shū)效果,對(duì)Turn.js翻書(shū)效果的實(shí)現(xiàn)進(jìn)行總結(jié),感興趣的小伙伴們可以參考一下2016-06-06JS獲取當(dāng)前時(shí)間的兩種方法小結(jié)
這篇文章主要給大家介紹了關(guān)于JS獲取當(dāng)前時(shí)間的兩種方法,在web開(kāi)發(fā)中,通過(guò)js獲取時(shí)間非常的常用,我這里做個(gè)總結(jié),需要的朋友可以參考下2023-09-09基于javascript中的typeof和類(lèi)型判斷(詳解)
下面小編就為大家?guī)?lái)一篇基于javascript中的typeof和類(lèi)型判斷(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10JavaScript使用math.js進(jìn)行精確計(jì)算操作示例
這篇文章主要介紹了JavaScript使用math.js進(jìn)行精確計(jì)算操作,結(jié)合實(shí)例形式分析了開(kāi)源庫(kù)math.js進(jìn)行高精度數(shù)學(xué)運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2018-06-06javascript showModalDialog 多層模態(tài)窗口實(shí)現(xiàn)頁(yè)面提交及刷新的代碼
javascript 多層模態(tài)窗口showModalDialog頁(yè)面提交及刷新2009-11-11詳細(xì)分析Javascript中創(chuàng)建對(duì)象的四種方式
這篇文章詳細(xì)介紹了Javascript中創(chuàng)建對(duì)象的幾種方式與每種方式的優(yōu)缺點(diǎn),其中包括工廠模式、構(gòu)造函數(shù)模式、原型模式和組合使用構(gòu)造函數(shù)模式和原型模式,有需要的小伙伴們一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2016-08-08jquery 時(shí)間戳轉(zhuǎn)日期過(guò)程詳解
這篇文章主要介紹了jquery 時(shí)間戳轉(zhuǎn)日期過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10javascript tips提示框組件實(shí)現(xiàn)代碼
一個(gè)簡(jiǎn)單的類(lèi)似title的提示效果,但現(xiàn)實(shí)內(nèi)容可以很豐富,以上js另存為tip.js,下面是使用的demo。2010-11-11深入理解JavaScript系列(7) S.O.L.I.D五大原則之開(kāi)閉原則OCP
本章我們要講解的是S.O.L.I.D五大原則JavaScript語(yǔ)言實(shí)現(xiàn)的第2篇,開(kāi)閉原則OCP(The Open/Closed Principle )。2012-01-01