该博友只有在摸鱼的时候在线
跟着做小白都能做出来~~首先搭建前端Vite 项目兼容性注意Vite 需要 Node.js 版本 14.18+,16+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。使用 npm或yarn、pnpm任意安装,个人推荐pnpm,原因如下快速:pnpm比传统方案(yarn, npm)安装包的速度快了两倍,甚至比yarn2,pnp模式还要快更严格高效:node_modules 中的文件是从一个单一的可内容寻址的存储中链接过来的,代码无法访问任意包monorepo:天然内置支持当仓库多包$ npm create vite@latest $ yarn create vite $ pnpm create vite1.安装并输入项目名称2.这里选择vue3.根据个人选择,这里选择js4.到这里就成功快速搭建完成vite3+vue3前端基础框架了,跟着下面的命令执行即可在vsocde编辑器中打开打开src目录,建立以下模块结构首先cofigjs这里是用来统一管理地址的,后期维护也方便只需要修改地址即可1.congfig-index.js中,这里写的是websokct// 域名地址(项目实地址)这里写的是本地的地址 const BASE_URL='127.0.0.1' // 端口号 const WS_PORT='8000' // WebSocket协议的URL export const WS_ADDRESS=`ws://${BASE_URL}:${WS_PORT}`这里的hooks是vue3借鉴的react函数式组件的优点2.hooks-index.js// 这里的index.js文件用于统一引用导出hooks的方法。更规范 import useWebSocket from './websocket' export { useWebSocket }hooks-websocket.js// 引用config-index.js封装好的WebSocket协议的URL import { WS_ADDRESS } from "../configs" // 封装一个使用websocket的高阶函数 // 这里的handleMessage用于接收及发送的消息 function useWebSocket(handleMessage) { // 建立连接(创建WebSocket对象): // const ws = new WebSocket(url, [protocol]);// url:服务器端地址;protocol:可选,指定可接受的子协议。 const ws = new WebSocket(WS_ADDRESS) const init = () => { bindEvent() } init() function bindEvent() { // ws.onopen():连接建立时触发的事件 ws.addEventListener('open', handleOpen, false); // ws.onclose():关闭连接方法 ws.addEventListener('close', handleClose, false); // ws.onerror():通信发生错误时触发 ws.addEventListener('error', handleError, false); // ws.onmessage(): 客户端接收服务器端发送的信息时触发 ws.addEventListener('message', handleMessage, false); } function handleOpen(e) { console.log('WebSocket open', e) } function handleClose(e) { console.log('WebSocket close', e) } function handleError(e) { console.log('WebSocket error', e) } return ws } export default useWebSocket3.pages-home.vue建立简单的聊天页面<script setup> // onMounted:挂载完数据 // reactive:响应式数据,reactive 参数必须是对象 (json / arr) // toRaw:响应式转化普通对象 // toRefs:降reactive中的响应式结构为一个个的ref响应式数据 import { onMounted, reactive, toRaw, toRefs } from "vue"; // 引用全局路由对象useRouter import { useRouter } from "vue-router"; // 引用hooks中封装好的高阶函数,用于消息发送 import { useWebSocket } from '../hooks' // 这里useWebSocket(handleMessage)用于接收消息 const ws = useWebSocket(handleMessage) const router = useRouter() // 定义state用于接收和发送的消息 const state = reactive({ msg: '', msgList: [] }) // 结构state const { msg, msgList } = toRefs(state) // 获取用户名,没有就跳转到登录页 let username = '' onMounted(() => { username = localStorage.getItem('username') if (!username) { router.push('/login') return } }) // 发送消息按钮 const sendMsg = () => { if (msg.length < 1) { return } // ws.send():发送信息的方法 // 由于后台接收的是JSON类型,这里发送的消息需要JSON.stringify ws.send(JSON.stringify({ id: new Date().getTime(), user: localStorage.getItem('username'), dateTime: new Date().getTime(), // 这里必须让msg失去响应式或深拷贝该值,否则造成发送的消息根据再次输入的消息发生变更 msg: toRaw(msg.value), })) // 发送完清空消息框 msg.value = '' } // 用于接收发送过来的消息 function handleMessage(e) { // 由于后台接收的是JSON类型,这里发送的消息需要JSON.parse const msgData = JSON.parse(e.data) // 向列表添加每条聊天消息 msgList.value.push(msgData) } </script> <template> <div> <ul> <li v-for="item in msgList" :key="item.id"> <p>用户名:{{ item.user }}</p> <p>{{ new Date(item.dateTime) }}</p> <p>消息:{{ item.msg }}</p> </li> </ul> <input type="text" placeholder="请输入消息" v-model="msg"> <button @click="sendMsg">发送</button> </div> </template> <style scoped> li p { text-align: left; } </style> pages-login.vue进入聊天室的页面<template> <div> <input type="text" placeholder="请输入用户名" v-model="userName"> <button @click="enterSocket">进入聊天室</button> </div> </template> <script setup> import { onMounted, ref } from "vue"; import { useRouter} from 'vue-router' const router=useRouter() const userName=ref('') onMounted(()=>{ userName.value=localStorage.getItem('username') // 简单的判断是否已进入聊天室 if(userName.value){ router.push('/home') return } }) // 进入聊天室 const enterSocket=()=>{ if(userName.value<6){ alert('用户名不小于6位') return } localStorage.setItem('username',userName.value) userName.value='' router.push('/home') } </script>4.router-index.js 由于框架是没有自带router的这里需要去pnpm i vue-router安装下import { createRouter, createWebHashHistory } from "vue-router"; import routes from "./router"; const router = createRouter({ history: createWebHashHistory(), routes, }); export default router;router-router.jsimport login from "../pages/login.vue"; // 路由列表 let routes= [ { path: "/login", name: "login", component: login, }, { path: "/home", name: "home", component: () => import("../pages/home.vue"), children: [], }, ]; export default routes; 5.App.vue<template> <router-view></router-view> </template> 6.main.jsimport { createApp } from 'vue' import './style.css' import App from './App.vue' import router from "./router"; createApp(App).use(router).mount('#app') 6.在当前目录下新建server目录,用于node服务进入server目录生成package.json然后安装包在安装websocket的插件wx接着是node的目录如一下就完成在package.json中配置启动命令如index.js中写// 使用CommonJS require引入 // CommonJS 中的 require/exports 和 ES6 中的 import/export 区别 // CommonJS 模块是运行时加载,ES6 Modules 是编译时加载并输出接口。 // CommonJS 输出是值的拷贝;ES6 Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变。 // CommonJs 导入的模块路径可以是一个表达式,因为它使用的是 require() 方法,甚至这个表达式计算出来的内容是错误的路径,也可以通过编译到执行阶段再出错; // 而ES6 Modules 只能是字符串,并且路径不正确,编译阶段就会抛错。 // CommonJS this 指向当前模块,ES6 Modules this 指向 undefined // ES6 Modules 中没有这些顶层变量:arguments、require、module、exports、__filename、__dirname const Websocket=require('ws') ;((Ws)=>{ // 建立ws服务地址,这里跟前端都类似了唯一的区别是connection和message(连接和消息) const server=new Ws.Server({port:8000,host:"0.0.0.0"}) const init=()=>{ bindEvent() } function bindEvent(){ server.on('open',function(event){ server.send('OpenBarScanner') }) server.on('close',handelClose) server.on('error',handelError) server.on('connection',handelConnection) } function handelOpen(){ console.log('handelOpen') } function handelClose(){ console.log('handelClose') } function handelError(){ console.log('handelError') } // 连接时提示消息并发送到客户端 function handelConnection(ws){ console.log('WebSocket Connection') ws.on('message',handelMessage) } // 用于接收发送的消息 function handelMessage(msg){ server.clients.forEach((c)=>{ // 由于node发送到客户端的消息是Blob类型,这里需要用Buffer.form().toString()转化为utf8类型识别消息 c.send(Buffer.from(msg).toString('utf8')) }) } init() })(Websocket)
查看详情安装包出现类似于Running postinstall script... 等错误Progress: resolved 566, reused 524, downloaded 0, added 0, done node_modules/.pnpm/registry.npmmirror.com+pngquant-bin@6.0.1/node_modules/pngquant-bin: Running postinstall script... node_modules/.pnpm/registry.npmmirror.com+gifsicle@5.2.0/node_modules/gif方案1.只需要找到对应的包名重新安装,在xxx.lock.yaml添加对应包名并锁定版本即可方案2.使用cnpm安装包再使用当前包命令安装一次即可,如yarn-lock,使用的yran,先用cnpm再用yarn
查看详情只能输入数字:<el-input v-model.number="num" oninput="value=value.replace(/[^\d]/g,'')" />禁止输入空格:<el-input v-model="name" @input="name = name.replace(/[ ]/g, '')" />禁止输入中文空格:<el-input v-model="code" @input="code = code.replace(/[\u4e00-\u9fa5]|[ ]/g, '')" />1.限制input输入框只能输入大小写字母、数字、下划线的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/[^\w_]/g,'');"> 2.限制input输入框只能输入小写字母、数字、下划线的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/[^a-z0-9_]/g,'');"> 3…限制input输入框只能输入数字和点的正则表达式:<el-input type="text" onkeyup="value=value.replace(/[^\d.]/g,'')">4.限制input输入框只能输入中文的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/[^\u4e00-\u9fa5]/g,'')"> 5.限制input输入框只能输入数字的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/\D/g,'')"> 6.限制input输入框只能输入英文的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/[^a-zA-Z]/g,'')">7.限制input输入框只能输入中文、数字、英文的正则表达式:<el-input onkeyup="value=value.replace(/[^\w\u4E00-\u9FA5]/g, '')"> 8.限制input输入框只能输入数字和字母的正则表达式:<el-inputt onKeyUp="value=value.replace(/[\W]/g,'')"> 9.限制input输入框除了英文的标点符号以外,其他的都可以中文,英文字母,数字,中文标点的正则表达式:<el-input type="text" onkeyup="this.value=this.value.replace(/^[^!@#$%^&*()-=+]/g,'')">10.限制input输入框只能输入数字代码(小数点也不能输入)的正则表达式:<el-input onkeyup="this.value=this.value.replace(/\D/g,'')" onafterpaste="this.value=this.value.replace(/\D/g,'')">11.限制input输入框只能输入数字,能输小数点.的正则表达式:<el-input onkeyup="if(isNaN(value))execCommand('undo')" onafterpaste="if(isNaN(value))execCommand('undo')"><el-input name=txt1 onchange="if(/\D/.test(this.value)){alert('只能输入数字');this.value='';}">或者<el-input type=text t_value="" o_value="" onkeypress="if(!this.value.match(/^[\+\-]?\d*?\.?\d*?$/))this.value=this.t_value;else this.t_value=this.value;if(this.value.match(/^(?:[\+\-]?\d+(?:\.\d+)?)?$/))this.o_value=this.value" onkeyup="if(!this.value.match(/^[\+\-]?\d*?\.?\d*?$/))this.value=this.t_value;else this.t_value=this.value;if(this.value.match(/^(?:[\+\-]?\d+(?:\.\d+)?)?$/))this.o_value=this.value" onblur="if(!this.value.match(/^(?:[\+\-]?\d+(?:\.\d+)?|\.\d*?)?$/))this.value=this.o_value;else{if(this.value.match(/^\.\d+$/))this.value=0+this.value;if(this.value.match(/^\.$/))this.value=0;this.o_value=this.value}">12限制input输入框只能输入字母和汉字的正则表达式:<el-input onkeyup="value=value.replace(/[\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[\d]/g,''))" maxlength=10 name="Numbers">13.限制input输入框只能输入英文字母和数字,不能输入中文的正则表达式:<el-input onkeyup="value=value.replace(/[^\w\.\/]/ig,'')">14限制input输入框只能输入数字和英文的正则表达式:<el-input onKeyUp="value=value.replace(/[^\d|chun]/g,'')">15限制input输入框小数点后只能有最多两位(数字,中文都可输入),不能输入字母和运算符号的正则表达式:<el-input onKeyPress="if((event.keyCode<48 || event.keyCode>57) && event.keyCode!=46 || /\.\d\d$/.test(value))event.returnValue=false">16.限制input输入框小数点后只能有最多两位(数字,字母,中文都可输入),可以输入运算符号:的正则表达式:<el-input onkeyup="this.value=this.value.replace(/^(\-)*(\d+)\.(\d\d).*$/,'$1$2.$3')">17.不可输入中文<el-input onkeyup="this.value=this.value.replace(/[\u4E00-\u9FA5]/g,'')">18.不可输入空格<el-input onkeyup="onkeyup="value = value.replace(/\s+/g, '')">
查看详情1.获取浏览器cookie值const cookie = name => `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift(); cookie('_ga'); Result: "GA1.2.1929736587.1601974046"2.将RGB转换为16进制const rgbToHex = (r, g, b) =>"#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); rgbToHex(0, 51, 255); Result: #0033ff`3.复制到剪切板(使用 navigator.clipboard.writeText 轻松将任何文本复制到剪贴板上)const copyToClipboard = (text) => navigator.clipboard.writeText(text); copyToClipboard("Hello World");4.检查日期是否有效const isDateValid = (...val) => !Number.isNaN(new Date(...val).valueOf()); isDateValid("December 17, 1995 03:24:00"); Result: true5.找出一年中的某一天(即给出一个日期,程序给出属于本年的第多少天)const dayOfYear = (date) =>Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24); dayOfYear(new Date()); Result: 2726.将字符串首字母大写const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1) capitalize("follow for more") Result: Follow for more7.计算两个日期之间相差的天数const dayDif = (date1, date2) => Math.ceil(Math.abs(date1.getTime() - date2.getTime()) / 86400000) dayDif(new Date("2020-10-21"), new Date("2021-10-22")) Result: 3668.清除所有cookieconst clearCookies = document.cookie.split(';').forEach(cookie => document.cookie = cookie.replace(/^ +/, '').replace(/=.\*/, `=;expires=${new Date(0).toUTCString()};path=/`));9.生成随机16进制const randomHex = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`; console.log(randomHex()); Result: #92b00810.数组去重const removeDuplicates = (arr) => [...new Set(arr)]; console.log(removeDuplicates([1, 2, 3, 3, 4, 4, 5, 5, 6])); Result: [ 1, 2, 3, 4, 5, 6 ]11.从URL中获取查询参数const getParameters = (URL) => {URL = JSON.parse('{"' + decodeURI(URL.split("?")[1]).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') +'"}');return JSON.stringify(URL)}; getParameters(window.location) Result: { search : "easy", page : 3 }12.从日期中获取“时分秒”格式的时间const timeFromDate = date => date.toTimeString().slice(0, 8); console.log(timeFromDate(new Date(2021, 0, 10, 17, 30, 0))); Result: "17:30:00"13.确认奇偶数//通过数据%2来判断并返回布尔类型const isEven = num => num % 2 === 0; console.log(isEven(2)); Result: True14、求平值const average = (...args) => args.reduce((a, b) => a + b) / args.length;average(1, 2, 3, 4); Result: 2.515.回到顶部(适用于网页右下角快捷返回功能)//通过将x、y设置为0来实现const goToTop = () => window.scrollTo(0, 0); goToTop();16.反转字符串const reverse = str => str.split('').reverse().join(''); reverse('hello world'); Result: 'dlrow olleh'17.检查数组是否为空//通过对数组长度判断来确定是否为空const isNotEmpty = arr => Array.isArray(arr) && arr.length > 0; isNotEmpty([1, 2, 3]); Result: true 18.获取用户选定的文本const getSelectedText = () => window.getSelection().toString(); getSelectedText();19.打乱数组const shuffleArray = (arr) => arr.sort(() => 0.5 - Math.random()); console.log(shuffleArray([1, 2, 3, 4])); Result: [ 1, 4, 3, 2 ]20、检查用户是否处于暗模式const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches console.log(isDarkMode) Result: True or False
查看详情1.第一个3D案例—透视投影相机Threejs如果想把三维场景Scene渲染到web网页上,还需要定义一个虚拟相机Camera,就像你生活中想获得一张照片,需要一台用来拍照的相机。a.透视投影相机PerspectiveCameraThreejs提供了正投影相机OrthographicCamera (opens new window)和透视投影相机PerspectiveCamera (opens new window)。本节课先给大家比较常用的透视投影相机PerspectiveCamera。透视投影相机PerspectiveCamera本质上就是在模拟人眼观察这个世界的规律。// 实例化一个透视投影相机对象 const camera = new THREE.PerspectiveCamera();b.相机位置.posiiotn生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。相机对象Camera具有位置属性.posiiotn,通过位置属性.posiiotn可以设置相机的位置。//相机在Three.js三维坐标系中的位置 // 根据需要设置相机位置具体值 camera.position.set(200, 200, 200); c.相机观察目标.lookAt()你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标。对于threejs相机而言,就是设置.lookAt()方法的参数,指定一个3D坐标。//相机观察目标指向Threejs 3D空间中某个位置 camera.lookAt(0, 0, 0); //坐标原点 camera.lookAt(0, 10, 0); //y轴上位置10 camera.lookAt(mesh.position);//指向mesh对应的位置d.判断相机相对三维场景中长方体位置你可以把三维场景中长方体mesh想象为一个房间,然后根据相机位置和长方体位置尺寸对比,判断两者相对位置。你可以发现设置相机坐标(200, 200, 200),位于长方体外面一处位置。// 长方体尺寸100, 100, 100 const geometry = new THREE.BoxGeometry( 100, 100, 100 ); const mesh = new THREE.Mesh(geometry,material); // 相机位置xyz坐标:0,10,0 mesh.position.set(0,10,0); // 相机位置xyz坐标:200, 200, 200 camera.position.set(200, 200, 200); e.定义相机渲染输出的画布尺寸你生活中相机拍照的照片是有大小的,对于threejs而言一样,需要定义相机在网页上输出的Canvas画布(照片)尺寸,大小可以根据需要定义,这里先随机定义一个尺寸。Canvas画布:课程中会把threejs虚拟相机渲染三维场景在浏览器网页上呈现的结果称为Canvas画布。// 定义相机输出画布的尺寸(单位:像素px) const width = 800; //宽度 const height = 500; //高度f.透视投影相机PerspectiveCamera:视锥体透视投影相机的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。// width和height用来设置Three.js输出的Canvas画布尺寸(像素px) const width = 800; //宽度 const height = 500; //高度 // 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面 const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);PerspectiveCamera参数介绍:PerspectiveCamera( fov, aspect, near, far )参数含义默认值fov相机视锥体竖直方向视野角度50aspect相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height1near相机视锥体近裁截面相对相机距离0.1far相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向2000
查看详情安装three官网包引入three.js<script type="module"> // 现在浏览器支持ES6语法,自然包括import方式引入js文件 import * as THREE from './build/three.module.js'; </script>three.js三个基本概念:场景Scene、相机Camera、渲染器Renderer1.三维场景Scene你可以把三维场景Scene (opens new window)对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。创建3D场景对象Sceneconst scene = new THREE.Scene();2.物体形状:几何体Geometry Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状{ 长方体(BoxGeomentry) 圆柱体(CylinderGeometry) 球体(SphereGeometry) 圆锥(ConeGeometry) 矩形平面(PlaneGeometry) 圆面图(CircleGeometry)}//创建一个长方体几何对象Geometryconst geometry = new THREE.BoxGeometry(100, 100, 100); 3.物体外观:材质Material如果你想定义物体的外观效果,比如颜色,就需要通过材质Material相关的API实现。threejs不同材质渲染效果不同,下面就以threejs最简单的网格基础材质MeshBasicMaterial (opens new window)为例给大家实现一个红色材质效果。{ 网格基础材质(MeshBasicMaterial) 网格漫反射材质(MeshLamberMaterial) 网格高光材质(MeshPhongMaterial) 物理材质(MeshStanderMaterial)(MeshPhysicalMaterial) 点材质(PointsMaterial) 线基础材质(LineBasicMaterial) 精灵材质(SpriteMaterial)}//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial({ color: 0xff0000,//0xff0000设置材质颜色为红色 }); 4.物体:网格模型Mesh实际生活中有各种各样的物体,在threejs中可以通过网格模型Mesh (opens new window)表示一个虚拟的物体,比如一个箱子、一个鼠标。// 两个参数分别为几何体geometry、材质materialconst mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh5.模型位置:position实际生活中,一个物体往往是有位置的,对于threejs而言也是一样的,你可以通过位置属性position定义网格模型Mesh在三维场景Scene中的位置。const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh //设置网格模型在三维空间中的位置坐标,默认是坐标原点 mesh.position.set(0,10,0);6.add()方法在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()方法,把网格模型mesh添加到三维场景scene中。scene.add(mesh); 代码小结<script type="module"> // es6引入three.js import * as THREE from "../build/three.module.js"; // 创建场景 const scene = new THREE.Scene(); // 创建物体形状(几何体) const geomeyry = new THREE.BoxGeometry(100, 100, 100); // 添加物体外观(材质) const material = new THREE.MeshBasicMaterial({ color: 0xff0000, }); // 创建网格模型 把形状外观放入模型中 const mesh = new THREE.Mesh(geomeyry, material); // 设置模型所在场景(Scene)中的位置 mesh.position.set(0, 10, 0); // 把模型添加到场景(Scene)中 scene.add(mesh); </script>
查看详情波浪扩展动画 /* css动画 */ @-webkit-keyframes ball-scale-multiple { 0% { -webkit-transform: scale(0); transform: scale(0); opacity: 0; } 5% { opacity: 1; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 0; } } @keyframes ball-scale-multiple { 0% { -webkit-transform: scale(0); transform: scale(0); opacity: 0; } 5% { opacity: 1; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 0; } } .ball-scale-multiple { position: relative; -webkit-transform: translate(0.2rem,0.14rem); -ms-transform: translate(0.2rem,0.14rem); transform: translate(0.2rem,0.14rem); z-index: 1; } .ball-scale-multiple>div:nth-child(2) { -webkit-animation-delay: 0.2s; animation-delay: 0.2s; } .ball-scale-multiple>div:nth-child(3) { -webkit-animation-delay: 0.4s; animation-delay: 0.4s; } .ball-scale-multiple>div { background-color: rgb(191, 235, 205); width: .15rem; height: .15rem; border-radius: 100%; -webkit-animation-fill-mode: both; animation-fill-mode: both; position: absolute; right: 0rem; top: -0.54rem; opacity: 0; margin: 0; width: .8rem; height: .8rem; -webkit-animation: ball-scale-multiple 1s 0s linear infinite; animation: ball-scale-multiple 1s 0s linear infinite; }光标移动动画@keyframes colorRight { 0% { background-position: -100% 0; } 100% { background-position: 130% 0; } } li:hover { p:first-child { animation: colorRight 1.2s; color: #222; background: @fontColor linear-gradient( -135deg, transparent 25%, transparent 40%, rgba(255, 255, 255, 1) 40%, rgba(255, 255, 255, 1) 60%, transparent 60%, transparent ); background-size: 20px 20px; background-repeat: no-repeat; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-position: -100% 0; } }边框闪动@keyframes borderRotate { 50% { height: 100%; width: 0; } 100% { height: 100%; width: 100%; } } li:hover { &::before { content: ""; position: absolute; width: 0; height: 0; border-left: 1px solid #98acff; border-bottom: 1px solid #98acff; z-index: 6; left: 0; bottom: 0; animation: borderRotate 0.5s forwards; } &::after { content: ""; position: absolute; width: 0; height: 0; border-top: 1px solid #98acff; border-right: 1px solid #98acff; z-index: 6; right: 0; top: 0; animation: borderRotate 0.5s forwards; } }
查看详情declare声明的变量和模块后,其他地方不需要引入,就可以直接使用了注意我们需要在配置文件下,引入声明文件{ "compilerOptions": { ... "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] ... }声明一个类型declare type Asd { name: string; }在include包含的文件范围内可以直接使用Asd这个typedeclare声明一个模块declare module '*.css';declare module '*.less';declare module '*.png';这样,我们可以在ts中引入相关的文件而不报错了declare声明一个变量declare var jQuery: (selector: string) => any;// 声明变量直接使用jQuery("#box")声明一个作用域declare namespace API{ interface ResponseObj { ... } ... }注意declare 与export 不要同级使用,不然的话,声明文件就需要导入了在声明文件中 type 与 interface 也可以不用加declare ,效果相同type myType = string | number;// 两者效果相同declare type myType = string | number;readonly用于标记一个属性是只读的,也就是不可修改的。什么时候要用命名空间?如果你发现自己写的功能(函数/类/接口等...)越来越多, 你想对他们进行分组管理就可以用命名空间, 下面先用"类"举例:什么是声明文件?声明文件就是给js代码补充类型标注. 这样在ts编译环境下就不会提示js文件”缺少类型”.Mock模拟接口数据模拟接口数据vue-meta主要用于管理HMTL头部标签,同时也支持SSR。vue-meta有以下特点:在组件内设置 metaInfo,便可轻松实现头部标签的管理metaInfo 的数据都是响应的,如果数据变化,头部信息会自动更新支持 SSR// 这里的?表示这个name属性有可能不存在class A { name?: string } interface B { name?: string }ts 中的type关键字type作用就是给类型起一个新名字,支持基本类型、联合类型、元祖及其它任何你需要的手写类型,常用于\color{red}{联合类型}type test = number; //基本类型 let num: test = 10; type userOjb = {name:string} // 对象 type getName = ()=>string // 函数 type data = [number,string] // 元组 type numOrFun = Second | getName // 联合类型type和interface的区别1、和接口一样,用来描述对象或函数的类型type User = { name: string age: number }; type SetUser = (name: string, age: number)=>void;vue3 中的 hooks 函数相当于 vue2 里面的 mixin 混入,不同在于 hooks 是函数。
查看详情