外国大气网站前端seo是什么意思
文章目录
- 小白 Vue 3 学习
- 一些名词
- 软件安装
- VSCode 中的一些设置
- 1. 关闭eslint检查
- tsconfig.json 配置文件
- 2. ts 文件引用报红
- 相对路径写法
- 常见图片格式和区别
- 目录结构
- JS/TS
- 1. prototype(原型对象)
- 2. 导入/导出
- 3. 去除字符串两端空格
- 4. 一些特殊语法糖
- 5. 深拷贝
- 6. 拼接两个数组
- 7. JSON
- DOM
- 一些常用属性
- HTML
- 结构代码展示
- 块级元素与行内元素
- 标签参考
- 容器标签
- 1. 文本标签
- 2. 图片标签
- 3. 超链接
- 4. 列表
- 5. 表格
- 6. 表单
- 7. 输入框
- 8.文本域
- 9.下拉框
- 10.label 标签
- 11.补充常用标签
- 12.框架标签
- 13.字符实体
- 全局属性
- CSS
- 样式
- 选择器
- 选择器优先级
- CSS 三大特性
- 字体属性
- 文本属性
- 列表属性
- 边框属性
- 表格属性
- 背景属性
- 鼠标相关属性
- 长度单位
- 元素的显示模式
- 修改元素显示模式
- 盒子模型的组成部分
- 隐藏元素的方式
- float 浮动
- overflow 处理内容溢出
- 元素之间的空白问题
- position 定位
- relative 相对定位
- absolute 绝对定位
- fixed 固定定位
- sticky 粘性定位
- z-index 定位的层级
- CSS3
- 长度单位
- 新增盒子属性
- 新增背景属性
- 多列布局
- 弹性布局
- cas_websited
- 0. 项目中学到的一些技巧
- 1. 指令
- 2. 协议对接
- 3. 请求服务器
- 4. 上拉加载历史消息(滑动条保持原高度)
- 5. 滑动条定位
- Vue 2
- 0. 总结
- 1. 初识 vue
- 2. 模板语法(V-bind,v-model)
- 3. vue 实例两种写法
- 4. Object.defineProperty() 给对象添加属性
- 5. Object.keys(对象)
- 6. v-on(事件处理)
- 7. 事件修饰符
- 8. 键盘事件
- 9. methods(方法)
- 10. computed(计算属性)
- 11. watch(监视属性)
- 12. 绑定样式
- 13. 条件渲染(v-show/v-if/v-else-if/v-else)
- 14. v-for (列表渲染)
- 15. 列表过滤和排序
- 16. set(object, key, value) 添加响应式属性
- 17. vue 监测数据总结
- 18. 收集表单数据示例
- 19. vue 内部指令
- 20. 生命周期
- 21. 非单文件组件
- 22. vm 与 vc
- 23. 单文件组件(.vue)
- 24. ref 属性
- 25. props 属性(父传子)
- 26. mixin(混合)
- 27. scoped(局部样式)
- 28. less
- 29. 路由
- 30. 浏览器本地存储
- 31. $emit $on 组件自定义事件(子传父)
- 32. nextTick()
- 33. 插槽
- Vue 3
- 注意事项
- 0. 创建 vue3 工程
- 1. setup 函数
- 2. ref 函数(为数据做响应式)
- 3. reactive 函数(处理对象响应式)
- 4. 新增/删除属性
- 5. computed 计算属性
- 6. watch 数据监视
- 7. watchEffect
- 8. 生命周期
- 9. hook (复用代码)
- 10. toRef 和 toRefs
- 11. shallowReactive
- 12. readonly 和 shallowReadOnly
- 13. toRaw 和 markRaw
- 14. provide 和 inject
- 15.响应式数据判断
- 16. Router (路由)
- 17. pinia 状态管理器使用
- 18. ref 获取 DOM 元素
小白 Vue 3 学习
一些名词
-
Node.js
Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境
-
NPM (Node Package Manager)
软件包仓库
软件安装
- Node
Node.js 安装配置 | 菜鸟教程 (runoob.com)
VSCode 中的一些设置
1. 关闭eslint检查
在 src/vue.config.js 文件中加入
module.exports = { // 避免Eslint报错 lintOnSave: false }
tsconfig.json 配置文件
{ "compilerOptions": {/* 访问 https://aka.ms/tsconfig.json 以阅读有关此文件的更多信息 *//* 基本选项 */"incremental": true, /* 启用增量编译 */"target": "ESNEXT", /* 指定 ECMAScript 目标版本:'ES3'、'ES5'(默认)、'ES2015'、'ES2016'、'ES2017'、'ES2018'、'ES2019'、'ES2020' 或 'ESNEXT'。 */"module": "commonjs", /* 指定模块代码生成:“none”、“commonjs”、“amd”、“system”、“umd”、“es2015”、“es2020”或“ESNext”。 */"lib": [], /* 指定要包含在编译中的库文件。 */"allowJs": true, /* 允许编译 javascript 文件。 */"checkJs": true, /* 报告 .js 文件中的错误。 */"jsx": "preserve", /* 指定 JSX 代码生成:'preserve'、'react-native' 或 'react'。 */"declaration": true, /* 生成相应的“.d.ts”文件。 */"declarationMap": true, /* 为每个对应的“.d.ts”文件生成一个源映射。 */"sourceMap": true, /* 生成相应的“.map”文件。 */"outFile": "./", /* 连接输出到单个文件。 */"outDir": "./", /* 将输出结构重定向到目录。 */"rootDir": "./", /* 指定输入文件的根目录。用于通过 --outDir 控制输出目录结构。 */"composite": true, /* 启用项目编译 */"tsBuildInfoFile": "./", /* 指定文件存放增量编译信息 */"removeComments": true, /* 不要向输出发出注释(删除除代码注释)。 */"noEmit": true, /* 不发出输出(不生成编译后的文件)。 */"noEmitOnError": true, /* 在输出js代码时,如果有错将不编译文件。 */"importHelpers": true, /* 从 'tslib' 导入发射助手。 */"downlevelIteration": true, /* 以“ES5”或“ES3”为目标时,为“for-of”、展开和解构中的迭代提供全面支持。 */"isolatedModules": true, /* 将每个文件转换为一个单独的模块(类似于 'ts.transpileModule')。 *//* 严格的类型检查选项 */"strict": true, /* 启用所有严格的类型检查选项。 在开发中,建议将stricet这类选项都开启。 */"strictNullChecks": true, /* 启用严格的空(undefined、null)检查,可以防止“未定义不是对象”。 建议开启*/"strictFunctionTypes": true, /* 启用函数类型的严格检查。 */"strictBindCallApply": true, /* 在函数上启用严格的“绑定”、“调用”、应用”方法。 */"strictPropertyInitialization": true, /* 启用对类中属性初始化的严格检查。 */"noImplicitThis": true, /* 使用隐含的“any”类型在“this”表达式上引发错误。 */"noImplicitAny": true, /* 使用隐含的“any”类型在表达式和声明上引发错误(主要用于控制变量、参数是否必须知道它们的类型【类型检查】),如果是将JavaScript迁移到TypeScript时,可以关闭此项,但不建议这样做。 */"alwaysStrict": true, /* 以严格模式解析并为每个源文件发出“使用严格”。 *//* 额外检查 */"noUnusedLocals": true, /* 报告未使用的本地人的错误。 */"noUnusedParameters": true, /* 报告未使用参数的错误。 */"noImplicitReturns": true, /* 不是函数中的所有代码路径都返回值时报告错误。 */"noFallthroughCasesInSwitch": true, /* 在 switch 语句中报告失败情况的错误。 *//* 模块分辨率选项 */"moduleResolution": "node", /* 指定模块解析策略:'node' (Node.js) 或 'classic' (TypeScript pre-1.6)。 */"baseUrl": "./", /* 解析非绝对模块名称的基目录。 */"paths": {}, /* 一系列将导入重新映射到相对于“baseUrl”的查找位置的条目。 */"rootDirs": [], /* 根文件夹列表,其组合内容代表运行时项目的结构。 */"typeRoots": [], /* 包含类型定义的文件夹列表。 */"types": [], /* 类型声明文件要包含在编译中。 */"allowSyntheticDefaultImports": true, /* 允许从没有默认导出的模块中默认导入。 这不会影响代码发出,只是类型检查。 */"esModuleInterop": true, /* 通过为所有导入创建命名空间对象,在 CommonJS 和 ES 模块之间启用发射互操作性。 暗示“allowSyntheticDefaultImports”。 */"preserveSymlinks": true, /* 不解析符号链接的真实路径。 */"allowUmdGlobalAccess": true, /* 允许从模块访问 UMD 全局变量。 *//* 源映射选项 */"sourceRoot": "", /* 指定调试器应该定位 TypeScript 文件而不是源位置的位置。 */"mapRoot": "", /* 指定调试器应该定位映射文件而不是生成位置的位置。 */"inlineSourceMap": true, /* 发出带有源映射的单个文件而不是单独的文件。 */"inlineSources": true, /* 在单个文件中与源映射一起发出源; 需要设置“--inlineSourceMap”或“--sourceMap”。 *//* 实验选项 */"experimentalDecorators": true, /* 启用对 ES7 装饰器的实验性支持。 */"emitDecoratorMetadata": true, /* 为装饰器的发射类型元数据启用实验性支持。 *//* 高级选项 */"skipLibCheck": true, /* 跳过声明文件的类型检查。 */"forceConsistentCasingInFileNames": true /* 禁止对同一文件的大小写不一致的引用。 */ } }
2. ts 文件引用报红
在 src 下创建文件 vite-env.d.ts
/// <reference types="vite/client" />interface ImportMetaEnv { readonly VITE_BASE_URL: string; readonly VITE_BASE_WS: string; // 更多环境变量... }interface ImportMeta { readonly env: ImportMetaEnv; }declare module "*.vue" { import type { DefineComponent } from "vue"; const component: DefineComponent<{}, {}, any>; export default component; }// 原生交互参数定义 interface Window { $message: any }declare module 'qrcodejs2-fix';
相对路径写法
‘./’ – 当前所在目录,可不写
‘…/’ – 上一级目录
‘/’ – 根目录
常见图片格式和区别
jpg | .jpg/.jpeg 有损压缩 占用空间小 |
---|---|
png | .png 无损压缩 占用空间略大 |
bmp | .bmp 不压缩 占用空间极大 |
gif | .gif 动态图 |
目录结构
api 与后端交互的接口,协议
assets
icon 图标
images 图片
style 样式components 公共组件
layouts 项目整体布局
loacles 多语言
router 路由
store 前端数据缓存 (项目中用 pinia)
types 定义数据结构
utils 工具类
view 视图
JS/TS
1. prototype(原型对象)
类似于继承,所有对象上都有 [[Prototype]] 属性,代表父类中的方法
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法
Date
对象从Date.prototype
继承。Array
对象从Array.prototype
继承。Person
对象从Person.prototype
继承。
var TestPrototype = function () {this.propA = 1;this.methodA = function() {return this.propA;}
}TestPrototype.prototype = {methodB: function() {return this.propA;}
}var objA = new TestPrototype();
var objB = new TestPrototype();objA.methodA() // 1
objA.methodB() // 1objA === objB // false
objA.methodA === objB.methodA // false
objA.methodB === objB.methodB // true
2. 导入/导出
-
导出
①默认导出:export default Test(导入时可指定模块任意名称,无需知晓内部真实名称) ②单独导出:export const name = "Bruce" ③按需导出:export { age, name, sex }(推荐) ④改名导出:export { name as newName }
-
导入
①默认导入:import Test from "test" ②整体导入:import * as Test from "test" ③按需导入:import { age, name, sex } from "test" ④改名导入:import { name as newName } from "test"
3. 去除字符串两端空格
//trim()例子
let str = ' hello '
console.log(str.trim()) //hello
4. 一些特殊语法糖
-
!
非空断言操作符
用于断言操作对象是非 null 和非 undefined 类型type HandleChange= () => void;function myFunc(onChange: HandleChange | undefined) {//如果不加!,会报错const a = onChange(); // Errorconst b = onChange!(); //OK }
-
?.
遇到 null 或 undefined 就可以立即停止表达式的运行
-
??
当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数
console.log(false ?? '哈哈哈') // false console.log(null ?? '哈哈哈') // 哈哈哈
5. 深拷贝
let deepObj = JSON.parse(JSON.stringify(obj));
6. 拼接两个数组
let arr = [1, 2]
let arr2 = [3, 4]arr = [...arr, ...arr2]console.log(arr)
// [1, 2, 3, 4]
7. JSON
-
JSON.parse() 将字符串转换为 JavaScript 对象。
// JSON.parse(text[, reviver]) // text: 一个有效的 JSON 字符串, reviver: 一个转换结果的函数, 将为对象的每个成员调用此函数。 let obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }', (key, value) => {if(key) {console.log(`读取到${key}属性,值为:${value}`)} }); // 读取到name属性,值为:runoob // Game.vue:13 读取到alexa属性,值为:10000 // Game.vue:13 读取到site属性,值为:www.runoob.com
-
JSON.stringify() 将 JavaScript 对象转换为字符串。
// JSON.stringify(value[, replacer[, space]]) // value: 要转换的 JavaScript 值(通常为对象或数组) replacer 传入每个成员的键和值。使用返回值 let obj = { "name":"runoob", "alexa":10000, "site":"www.runoob.com" }let myJson = JSON.stringify(obj) console.log(myJson) // {"name":"runoob","alexa":10000,"site":"www.runoob.com"}
DOM
一些常用属性
-
scrollTop, scrollHeight, clientHeight
- scrollTop:元素顶部到元素可视区域顶部的像素距离(可读写)。
- scrollHeight:类似于clientHeight,但包括由于overflow属性不可见内容的高度。
- clientHeight:元素的像素高度,包括盒子内容content和内边距padding, 不包括边框外边距和水平滚动条(只读)
滚动到底部判断:element.scrollHeight-element.scrollTop=element.clientHeight
HTML
结构代码展示
<!-- 在 VSCode 中键入一个 '!' 回车可自动补齐 --><!DOCTYPE HTML> <!-- HTML5标准网页声明 -->
<HTML lang="en"> <!-- HTML为根标签,代表整个网页 zh-CN --><head> <!-- head为头部标签,一般用来描述文档的各种属性和信息, 包括标题等--><meta charset="UTF-8"> <!-- 设置字符集为utf-8 --><title>my HTML</title> <!-- 设置浏览器的标题 -->
</head><!-- 网页所有的内容都写在body标签内 -->
<body> 我的第一个HTML网页
</body></HTML>
块级元素与行内元素
块级元素:独占一行,块级元素内能写任何元素
行内元素:不独占一行,行内元素内能写行内元素,但不能写块级元素
标签参考
容器标签
<div> </div>
1. 文本标签
常用:
em | 用户着重阅读的内容 |
---|---|
strong | 表示文本十分重要,一般用粗体显示 |
span | 元素是短语内容的通用行内容器,并没有任何特殊语义。 |
不常用:
cite | 表示一个作品的引用,且必须包含作品的标题 |
---|---|
dfn | 特殊术语,或专属名词 |
del 与 ins | 删除的文字内容【与】插入文档中的文本 |
sub 与 sup | 下标文字【与】上标文字 |
code | 一段代码 |
samp | 标识计算机程序输出 |
kbd | 键盘输入元素,用于表示用户输入 |
addr | 联系信息,说明,配合 title 属性使用 |
bdo | 改写了文本的方向性,配合 dir 属性使用 |
var | 变量 |
address | 某个人或某个组织(等等)的联系信息。 |
2. 图片标签
标签名 | 属性 | 说明 |
---|---|---|
img | src(相对路径),alt(图像描述,利于搜索) | 单标签 |
3. 超链接
标签名 | 属性 | 说明 |
---|---|---|
img | href(跳转网址),target(新页面从哪打开) download=“name”(将链接的 URL 视为下载资源) | 单标签,超链接 |
通过超链接跳转锚点:
<body><a href="#5">测试超链接跳转锚点</a><p>我是锚点1</p><p>我是锚点2</p><p>我是锚点3</p><p>我是锚点4</p><p id="5">我是锚点5</p>
</body><a href="#">回到顶部</a> <!-- 回到顶部 -->
超链接唤起指定应用:
<!-- 唤起设备拨号 -->
<a href="tel:10086">电话联系</a>
<!-- 唤起设备发送邮件 -->
<a href="mailto:10086@qq.com">邮件联系</a>
<!-- 唤起设备发送短信 -->
<a href="sms:10086">短信联系</a>
4. 列表
-
4.1 ol (有序列表)
<h2>把大象装进冰箱分几步</h2> <!-- 有序列表 --> <ol><li>打开冰箱门</li><li>大象放进去</li><li>关上冰箱门</li> </ol>
-
4.2 ul (无序列表)
<h2>我想去的几个城市</h2> <!-- 无序列表 --> <ul><li>云南</li><li>成都</li><li>重庆</li> </ul>
-
dl (自定义列表)
<h2>我想去的几个城市</h2> <!-- 自定义列表 --> <dl><dt>做好笔记</dt><dd>好记性不如烂笔头</dd><dt>多加练习</dt><dd>实践出真知</dd><dt>别怕失败</dt><dd>失败是成功之母</dd> </dl>
-
注意点
-
列表可嵌套使用
<ul><li>云南</li><li>成都</li><li><span>上海</span><ul><li>外滩</li><li>东方明珠</li><li>迪士尼乐园</li></ul></li> </ul>
-
自定义列表一个术语可有多个描述
<dl><dt>做好笔记</dt><dd>好记性不如烂笔头</dd><dd>笔记可以是电子版或纸质版</dd> </dl>
-
5. 表格
<!--
table属性: border:外边框厚度 weight,height:宽高,不影响表头和脚注 cellspacing:边框间距离
thead属性: allign:水平布局方式 vallign:垂直布局方式
tbody属性:
tfoot属性:
th属性: colsspan:跨列 rowspan:跨行
-->
<table border="1"><caption>学生信息</caption> <!-- 表格标题 --><!-- 表格头部 --><thead> <tr> <!-- 表格中的行 --><th>姓名</th> <!-- 表头单元格 --><th>性别</th><th>年龄</th><th>民族</th><th>政治面貌</th></tr></thead><!-- 表格主体 --><tbody><tr><td>张三</td> <!-- 表体单元格 --><td>男</td> <td>18</td> <td>汉族</td> <td>团员</td> </tr><tr><td>李四</td><td>女</td> <td>20</td> <td>满族</td> <td>群众</td> </tr></tbody><!-- 表格脚注 --><tfoot><tr><td></td> <!-- 表体单元格 --><td></td><td></td><td></td><td>共计:4人</td></tr></tfoot>
</table>
6. 表单
表示文档中的一个区域,此区域包含交互控件,用于向 Web 服务器提交信息
<!-- 属性:action:表单提交地址, -->
<form action="https://search.jd.com/search">用户名:<!-- 禁用 --><input name="userName" value="hwm" disabled>性别:<!-- 单选框 --><input type="radio" name="gender" value="male" checked>男 <!-- checked 默认 --><input type="radio" name="gender" value="female">女<br>爱好:<!-- 复选框 --><input type="checkbox" name="hobby" value="smoke" checked>抽烟<input type="checkbox" name="hobby" value="drink">喝酒<input type="checkbox" name="hobby" value="perm">烫头<br>其它:<!-- 文本域--><textarea name="other" cols="23" row="3"></textarea><br>籍贯:<!-- 下拉框 --><select name="place"><option value="湘" selected>湖南</option><option value="粤">广东</option><option value="鲁">山东</option></select><br>用户不可见区域:<!-- 因隐藏域 --><input type="hidden" name="tag" value="abc"><br><input type="submit" value="提交"><input type="reset" value="重置"><button type="button">普通按钮</button> <!-- 普通按钮 -->
</form><!-- 分类 -->
<fieldset><legend> ... </legend>...
</fieldset>
7. 输入框
<!-- 属性
type:类型 value:默认值 maxlength:最大输入长度
-->
8.文本域
<textarea name="other" cols="23" row="3"></textarea>
9.下拉框
<select name="place"><option value="湘" selected>湖南</option><option value="粤">广东</option><option value="鲁">山东</option>
</select>
10.label 标签
<!-- 表示用户界面中某个元素的说明 -->
11.补充常用标签
<!-- 换行标签 -->
<br><!-- 分割线 -->
<hr><!-- 按原文显示 -->
<pre>
12.框架标签
<!-- 将另一个 HTML 页面嵌入到当前页面中 -->
<!-- 可用 name 属性与<a>或<form>的 target 属性配合使用 -->
<iframe src="https://cn.bing.com/" width="1920" height="1080" frameborder="0">
</iframe>
13.字符实体
参考链接
全局属性
-
id
给标签指定唯一标识,id 不可重复
作用:可让标签相关联
注意:,,,
CSS
h1 {color: red;font-size: 40px;
}
h2 {color: green;font-size: 60px;
}
样式
行内样式:写在标签内,只对改标签生效
内部样式:写在 html 文件 内,对整个 html 文件生效
外部样式:将 css 样式单独写在一个 .css 文件中,在 html 中使用 标签链接优先级:行内 > 内部样式 = 外部样式 (后渲染的覆盖前渲染的)
选择器
-
通配选择器
* {属性名: 属性值; }
-
元素选择器
/* 为页面某种元素统一设置样式 */ 标签名 {属性名: 属性值; }
-
类选择器
/* 为页面所有类名为 className 的元素设置样式 */ .className {属性名: 属性值; }
为标签同时设置两个类
<p class="fish animal">
-
id 选择器
#IDName {属性名: 属性值; }/* html */ <h1 id="idName">id 选择器</h1>
-
交集选择器
5.1 元素选择器 & 类选择器标签名.className {属性名: 属性值; }
-
并集选择器
,类选择器 ,元素选择器 ,id 选择器 {属性名: 属性值; }
-
后代选择器(子类,子类的子类)
父元素 后代元素 {属性名: 属性值; }
-
子代选择器
父元素>子元素 {属性名: 属性值; }
-
兄弟选择器
/* 往下找紧紧相邻的兄弟元素 */ 元素 + 兄弟元素 {属性名: 属性值; }/* 元素后所有兄弟元素 */ 元素 ~ 兄兄弟元素 {属性名: 属性值; }
-
属性选择器
/* 1.选中具有 title 属性的元素 */ [title] {属性名: 属性值; }/* 2.选中具有 title 属性,且 title 属性为 "tt" 的元素 */ [title="tt"] {属性名: 属性值; }/* 3.选中具有 title 属性,且 title 属性为 "tt" 开头的元素 */ [title^="tt"] {属性名: 属性值; }/* 4.选中具有 title 属性,且 title 属性为 "tt" 结尾的元素 */ [title$="tt"] {属性名: 属性值; }/* 4.选中具有 title 属性,且 title 属性为包含 "tt" 的元素 */ [title*="tt"] {属性名: 属性值; }
-
动态伪类选择器
/* 和类相似,但不是类,元素特殊状态的一种描述 *//* link visited 是 <a> 的特有状态 focus 是表单类特有 *//* -------------------- 动态伪类 ---------------------- */ /* 没有访问过的 a 元素 */ a:link {属性名: 属性值; } /* 访问过的 a 元素 */ a:visited {属性名: 属性值; } /* 鼠标悬浮的 a 元素 */ a:hover {属性名: 属性值; } /* 激活状态的 a 元素 */ a:active {属性名: 属性值; } /* 获取焦点 */ input:focus {属性名: 属性值; }/* -------------------- 结构伪类 ---------------------- */ /* 第一个子元素是否满足条件,满足渲染 */ div>p:first-child {属性名: 属性值; } /* 最后一个子元素是否满足条件,满足渲染 */ div>p:last-child {属性名: 属性值; } /* 第 n 个子元素是否满足条件,满足渲染,子元素从 1 开始计算*/ /* n 从 0 开始增加 */ div>p:nth-child(n) {属性名: 属性值; }/* 只在指定类型中找,非此类型不参与 */ :fist-of-type :last-of-type :nth-of-type/* -------------------- 否定伪类 ---------------------- */ :div>p:not(.fial) {属性名: 属性值; }/* -------------------- UI伪类 ---------------------- */ :checked {属性名: 属性值; } :disabled {属性名: 属性值; }/* -------------------- 目标伪类 ---------------------- */ :target { /* 锚点指向的元素 */属性名: 属性值; }/* -------------------- 语言伪类 标签 lang 属性 ---------------------- */ :lang(语言类型) {属性名: 属性值; }/* -------------------- 伪元素(像元素,但不是,是元素中的一些特殊位置)选择器 ---------------------- */ /* 第一个字母 */ ::first-letter {属性名: 属性值; } /* 第一行 */ ::first-line {属性名: 属性值; } /* 选中的文字 */ ::selection {属性名: 属性值; } /* 提示颜色 */ ::placeholder {属性名: 属性值; } /* 在元素最前面 */ ::before {content: Y属性名: 属性值; } /* 在元素最后面 */ ::after {content: Y属性名: 属性值; }
选择器优先级
简单的:!important > 行内样式 > id 选择器 > 类选择器 > 元素选择器 > 通配选择器 > 继承样式
公式计算:
(a,b,c)
a:id 选择器个数
b:类,伪类,属性 选择器个数
c:元素,伪元素 选择器个数
从 a -> b -> c 依次比较,大的优先级高可在 vscode 中将鼠标放到选择器上查看
CSS 三大特性
-
层叠性
-
继承性
元素自动拥有其父元素,或其祖先元素上设置的某些样式
规则:优先继承近的
常见可继承属性:text-?? font-?? line-?? color
可在 MDN 查看元素是否可继承 -
优先级
字体属性
-
font-size 字体大小
- 可以给 body 指定整个页面的文字大小
- 可在 设置 -> 外观 中调整字体最小字号和字体默认大小
-
font-family 字体
-
font-style 字体风格
- italic 斜体
- normal 正常
-
font-weight 字体粗细
-
lighter 细的
-
normal 正常
-
bold 粗体
-
数字 100 -900(跟字体设计精度有关)
-
-
字体复合属性
body {font: font-style font-weight font-size/line-height font-family; }
- 使用 font 属性时,必须按上面的语法格式中的顺序书写,不得更换顺序,并且各个属性间以空格隔开。
- 不需要设置的属性可以省略(取默认值),但必须保留 font-size 和 font-family 属性,否则 font 属性不起作用。
文本属性
-
color 文本颜色
/* color: red; */ /* color: rgb(255, 0, 0); */ /* color: rgba(255, 0, 0, 0.5); */ color: #ff0000;
-
文本间距
- letter-spacing 字符间距
- word-spacing 单词间距(根据空格识别单词)
- 可给负值,缩小字符间距
-
text-decoration 文本修饰
属性 说明 overline 上划线 underline 下划线 line-through 删除线 wavy 波浪线 text-decoration: overline underline wavy blue 5px;
-
text-indent 文本缩进
text-indent: 40px;
-
文本对齐
-
text-align 水平对齐
left center right -
line-height 行高
-
文本垂直对齐
顶部:默认
居中:单行文字:height = line-height
底部:单行文字:line-height = (height x 2) - font-size -x
-
-
vertical-align
参考 MDN
该属性定义行内元素的基线相对于该元素所在行的基线的垂直对齐。
只对行内元素、行内块元素和表格单元格元素生效:不能用它垂直对齐块级元素
列表属性
- list-style-type:列表符号
- list-style-position:指定标记框在主体块框中的位置
- list-style-image:自定义列表元素标记的图片
边框属性
- border-width
- border-style
- border-color
- border(复合属性)
表格属性
- table-layout:控制表格列宽
- border-spacing:控制单元格间距
- border-collapse:合并相邻单元格的边框
背景属性
- background-color:背景颜色,transparent
- background-image:会平铺整个区域
- background-repeat:设置背景图片的重复方式
- background-position:设置背景图片的位置
鼠标相关属性
- cursor:光标样式
长度单位
- px:像素
- em:相对于当前元素的 font-size 的倍数,先找父元素的 font-size,最后浏览器默认 font-size
- %:相对于父元素的百分比
元素的显示模式
-
块元素(block)
特点:
- 在页面独占一行
- 默认宽度:撑满整个父元素
- 默认高度:由内容撑开
- 可以通过 css 修改宽高
代表元素:
- 主体结构标签:
- 排版标签:
-
- 列表标签:
-
- 表格相关标签:
-
-
行内元素(inline)
特点:
- 在页面不独占一行
- 默认宽度:由内容撑开
- 默认高度:由内容撑开
- 无法通过 css 修改宽高
代表元素:
- 文本标签:br em strong sup sub del ins
-
行内块元素(inline-block)
特点:
- 在页面不独占一行
- 默认宽度:由内容撑开
- 默认高度:由内容撑开
- 可以通过 css 修改宽高
代表元素:
- 图片:
- 单元格:
- 表单控件:
修改元素显示模式
- display
block: 块元素,none:不显示,inline:行内元素,inline-block:行内块元素
盒子模型的组成部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l27Gr60g-1689749017403)(C:\Users\cli08\AppData\Roaming\Typora\typora-user-images\image-20230712181229789.png)]
- content(内容):元素中的文本或后代元素都是它的内容
- padding(内边距):紧贴内容的补白区域
- border(边框):盒子的边距
- margin(外边框):盒子与外界的距离
盒子的大小 = content + 左右 padding + 左右 border
注意:外边距 margin 不会影响盒子的大小,但会影响盒子的位置
-
content
min-width,max-width,min-height,max-height
width,height
-
padding
padding-left…
padding:(上下)(左右) (上)(右)(下)(左)
注意:padding 不能为负数,行内元素上下内边距设置会不占位置
-
border
border-width,border-color,border-style
border:width,style,color
border-left-width…,border-left-colo…,border-left-style…
border-left…
-
margin
属性同 padding
隐藏元素的方式
- visibility:设置为隐藏元素占位
- display:设置为隐藏不占位
float 浮动
指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它
属性值:left | right
浮动后的特点
- 脱离文档流
- 默认宽高被内容撑开,可手动设置宽高
- 不会独占一行,能与其它元素公用一行
overflow 处理内容溢出
overflow: visible 可见
overflow: hidden 隐藏
overflow: scroll 滚动条
overflow: auto 超出显示滚动条overflow-x
overflow-y
元素之间的空白问题
产生原因:行内元素,行内块元素,彼此之间的换行会被浏览器解析为一个空白字符
解决方案:给父元素设置 font-size: 0,再给需要显示文字的元素单独设置字体大小
position 定位
定位的元素层级较高
都写了定位,后写的层级高
relative 相对定位
left | top | right | bottom
特点:
- 参考系:相对于正常位置的偏移
- 不会脱离文档流,不会对其它元素产生影响
absolute 绝对定位
left | top | right | bottom
特点:
- 脱离文档流
- 绝对定位和浮动不能一起用
- 开启绝对定位后成为定位元素:宽高由内容撑开,宽高可更改
- 参考系:包含块
- 包含块:
- 对于没有脱离文档流的元素,包含块为其父元素
- 对于脱离文档流的元素,包含块是第一个拥有定位属性的祖先元素(如果内衣,包含块就是整个页面)
fixed 固定定位
left | top | right | bottom
特点:
- 脱离文档流
- 参考系:视口,元素一直在视口中
- 不能和浮动同时使用
sticky 粘性定位
left | top | right | bottom(胶水生效位置)
特点:
- 不脱离文档流
- 参考系:离它最近的一个拥有“滚动机制的祖先元素”
- 可以和浮动一起设置(不推荐)
z-index 定位的层级
auto 默认 | 0 | 1 | 2
只能在设置了定位的元素上设置
CSS3
长度单位
- vw:视口宽度的百分比
- vh:视口高度的百分比
新增盒子属性
-
box-sizing
border-box:设置的边框和内边距的值是包含在 width 内的,如果你将一个元素的 width 设为 100px,那么这 100px 会包含它的 border 和 padding
content-box:默认值。如果你设置一个元素的宽为 100px,那么这个元素的内容区会有 100px 宽,并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。
-
resize
both | horizontal | vertical | none 用于设置元素是否可调整尺寸,以及可调整的方向。
要设置 overflow
-
box-dhadow
/* x 偏移量 | y 偏移量 | 阴影颜色 */ box-shadow: 60px -16px teal;/* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影颜色 */ box-shadow: 10px 5px 5px black;/* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */ box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
-
opacity
透明度
新增背景属性
-
background-orgin
背景图原点,起始点
padding-box | content-box | border-box -
background-clip
对背景图修剪,显示背景图区域
padding-box | border-box | content-box | text -
backgronud-size
背景图尺寸
宽高 | %% | contain(宽高等比例缩放,背景再重复) | cover(宽高等比例缩放,宽或高自适应后截取)
多列布局
column-count:直接指定列数
column-width:指定每列宽度
cloumn-gap:列间距
column-rule:边框像素,样式,颜色
column-span:跨列
弹性布局
特点:子元素块状化,变为块元素
属性值:display:flex(弹性布局) | inline-flex(不常用)
-
主轴方向
flex-direction:row(行,左到右) | column(列,上到下) | row-reverse(右到左) | column-reverse(下到上)
-
主轴换行方式
flex-wrap:nowrap(不换行,压缩子元素) | wrap(自动换行,副轴平分空间) | wrap-reverse(反向换行)
-
主轴对齐方式
justify-content:
- flex-start:主轴起始位置
- flex-end:主轴结束位置
- center:中间对齐
- space-around:子项均匀分布,子项与子项之间的距离,是子项与边缘之间距离的两倍
- space-between:子项均匀分布,子项与子项之间的距离是相等的,子项与边缘无距离
- space-evenly:子项均匀分布
-
侧轴对齐方式
单行:align-item:
- flex-start:侧轴起始位置对齐
- flex-end:侧轴结束位置对齐
- center:侧轴中间位置对齐
- stretch(默认值):拉伸到整个父容器
多行:align-content:
- flex-start:侧轴起始位置对齐
- flex-end:侧轴结束位置对齐
- center:侧轴中间位置对齐
- space-around:同主轴
- space-between:同主轴
- space-evenly:同主轴
- stretch(默认值):同侧轴
-
水平垂直居中
方案一: justify-content: center align-items:center 方案二: 父容器:display: flex 子项:margin:auto
cas_websited
0. 项目中学到的一些技巧
-
给组件传值
<isLike :unlike="msgInfo.unlike" :like="msgInfo.like" :status="msgInfo.status" size="s" :faqID="faqID"></isLike>
const props = defineProps({// 踩unlike:{type:Number,dafault:0},// 点赞like:{type:Number,dafault:0},// 当前用户是否点赞status:{type:Number,default:0},// 图标尺寸 S Msize:{type:String,default:'s'},//faqidfaqID:{type:Number,default:1} })
1. 指令
npm run dev
npm run dev --mode test
2. 协议对接
- 确定开发环境
.env.development 本地开发环境
.env.production 生产环境
.env.test 测试环境- 修改对应环境下的 VITE_BASE_URL
3. 请求服务器
// 示例
interface msgListReq {UpDown: number | null,ID: number | null,
}export const apiMsgList = (data: msgListReq) => {return axios.request({url: "/msg/list",method: "post",data})
}export const result = (data: {OrderID: number, Count: number}) => {return axios.request({url: "/order/info",method: "post",data,})
}// 使用
apiMsgList({UpDown: 0, ID: msgList.value[0].ID}).then(res => {if(!res.data.List || res.data.List.length == 0) return // 历史消息为空,不处理res.data.List.reverse()msgList.value = [...res.data.List, ...msgList.value]
})
4. 上拉加载历史消息(滑动条保持原高度)
let scrollTop = document.documentElement.scrollTop;//滚动高度
let clientHeight = document.documentElement.clientHeight;//可视高度
let scrollHeight = document.documentElement.scrollHeight;//内容高度
function handleScroll(e:any) {// 滚动条到顶部时,出现loading状态,接口请求完毕,关闭loading状态if (msg.value!.scrollTop <= 0) {scrollVal = msg.value!.scrollHeight // 记录下当前的滑动条高度apiMsgList({ UpDown: 0, ID: msgList.value[0].ID }).then(res => {show.value = trueif (!res.data.List || res.data.List.length == 0){ // 历史消息为空,不处理show.value = falsereturn} setTimeout(() => {res.data.List.reverse()msgList.value = [...res.data.List, ...msgList.value]nextTick(() => {msg.value!.scrollTop = msg.value!.scrollHeight - scrollValshow.value = false})}, 500); })} else {show.value = false}
}
5. 滑动条定位
nextTick(() => {var targetDom: any = document.querySelector('.isActive')var boxDom: any = document.querySelector('.main_left')var st = targetDom.getBoundingClientRect().topboxDom.scrollTop = boxDom.scrollTop + st - 75 - 70 - 15
})
Vue 2
0. 总结
-
data
Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化
: ‘ v m . : `vm. :‘vm.data访问原始数据对象。访问
vm.a等价于访问
vm. d a t a . a ‘ 。 v m . data.a`。 vm. data.a‘。vm.watch -
el
只在用
new
创建实例时生效
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
1. 初识 vue
<!-- 1.想让 Vue 工作,就必须创建一个 Vue 实例,且要传入一个配置对象2.root容器里的代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法3.root容器里的代码被称为 Vue 对象4.Vue 实例与容器一一对应5.真实开发中只有一个 Vue 实例,并且会配合组件一起使用6.容器中的 {{}} 可写 vue 实例中对象的属性或者 js 表达式7.data 中的数据发生改变,魔板中用到该数据的地方会自动更新8.在浏览器中可通过 vue.js devtools Root 中查看或临时更改 data 中的属性9.Vue 实例数据结构可嵌套
--><div id="root"><h1>Hello, {{name.toUpperCase()}},{{age}},{{1+1}}</h1><h1>学校:{{school.name}},地址:{{school.address}}</h1>
</div><script type="text/javascript">Vue.config.productionTip = false // 阻止 vue 在启动时生成提示// data 对象中的数据会做数据代理,既 get set,method 对象中的数据不会坐数据代理,里面存放方法// method 中的方法不要用箭头函数,会导致 this 指向对象为 window (正常为 vm)const x = new Vue({el:'#root', // el 指定当前 vue 实例为哪个容器服务,值为 css 选择器data:{ // data 用于存储数据,数据提供给 el 所指定的容器使用 {{name}}name:"hwm",age:18,school:{name:"hufe",address:"changsha"},methods: {showInfo1(event) {console.log(this)console.log(event)},showInfo2(event, num) {console.log(event, num)}}}})
</script>
2. 模板语法(V-bind,v-model)
-
插值语法
<h1>Hello, {{name.toUpperCase()}},{{age}},{{1+1}}</h1>data:{name:"hwm",age:18, }
-
指令语法
-
v-bind(:):
单向绑定
响应式的更新html属性(在元素节点的属性上绑定 vue 的 data 数据)
语法糖:v-bind: 简写为 :
注意事项:v-bind: 后的 “” 中的值不是表示字符串,而是表示 vue 中的 data 中的属性<a v-bind:href="url" target="_blank">点我,去百度</a> <!-- vue 中的 date --> data:{url:"http://www.baidu.com", }
-
v-model(在表单中收集 value 值)
双向绑定
只能应用在表单类元素上
语法糖:省略 value, v-model=“”
修饰符:.number .lazy(失去焦点再收集) trim(去除前后空格)<div id="root">单向数据绑定:<input type="text" :value="iV1"/></br>双向数据绑定:<input type="text" v-model="iV1"/></br>修饰符:<input type="number" v-model.number="age"/></br><input type='text' v-model.lazy="age"/></br> </div>data:{iV1: "hwm", },
-
3. vue 实例两种写法
// 第一种
new Vue({el:"#root", // el 指定当前 vue 实例为哪个容器服务,值为 css 选择器data:{ // data 用于存储数据,数据提供给 el 所指定的容器使用 {{name}}name:"ming",}
})// 第二种
const vm = new Vue({data(){return {name:"ming",}}
})
vm.$model("#root")
4. Object.defineProperty() 给对象添加属性
注意:通过此方法添加的属性,不在枚举内
定义(添加、修改)对象的属性
Object.defineProperty(对象,属性名,配置项(value))
let student = {name:"ming",sex:"男",
}
let num = 18Object.defineProperty(student, "age", {value:18,enumerable: true, // 控制属性是否可枚举,默认 falsewritable: true, // 控制属性是否可更改,默认 falseconfigurable: true, // 控制属性是否可删除,默认 falseget() { // 获取 age 属性时调用return num},set(value) { // 设置 age 属性时调用num = value}
})
5. Object.keys(对象)
遍历获取到对象上的所有 key 属性
6. v-on(事件处理)
事件处理
v-on 可简写 @
new Vue({el:"#root",data() {return {}},methods: {showInfo1(event) {console.log(event)},showInfo2(event, num) {console.log(event, num)}}
})// 使用
/* <div id="root"><button @click="showInfo1">Hello111(不传参,默认传event)</button><button @click="showInfo2($event, 66)">Hello222(传参, $event 是默认值)</button>
</div> */
7. 事件修饰符
- prevent:阻止默认事件
- stop:阻止事件冒泡(阻止一层冒泡)
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有 event.target 是当前操作的元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件的回调执行完成
<button @click.once="addPerson">添加一个老刘</button>
8. 键盘事件
@keyup
@keydown
<!-- 为提供别名的按键可使用按键原始的 key 绑定,两个单词组成的键要写为 caps-lock -->
<input type="text" @keyup.enter="showInfo">
9. methods(方法)
<button @click.once="addPerson">添加一个老刘</button>
const vm = new Vue({el: '#app',data: {firstName:"张",lastName:"三",},methods: {addPerson(e) {console.log(e)}},
}
10. computed(计算属性)
const vm = new Vue({el: '#app',data: {firstName:"张",lastName:"三",},methods: {showInfo(e) {console.log(e)}},computed: {fullName: {// get 作用:当有人读取 fullName 时,get 就会被调用,且返回值就作为 fullname 的值// 调用时机: 1.初次读取 fullname 时 2.所依赖的数据发生变化时// this 指向 vmget() {return this.firstName + "-" + this.lastName},// 当 fullName 被修改时调用set(value) {const arr = value.split("-")this.firstName = arr[0]this.lastName = arr[1]}},// 简写,当没有 set 函数时可简写成fullName_simple() {return this.firstName + "-" + this.lastName},}
})
11. watch(监视属性)
<body><!-- 1.想让 Vue 工作,就必须创建一个 Vue 实例,且要传入一个配置对象2.root容器里的代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法3.root容器里的代码被称为 Vue 对象4.Vue 实例与容器一一对应5.真实开发中只有一个 Vue 实例,并且会配合组件一起使用6.容器中的 {{}} 可写 vue 实例中对象的属性或者 js 表达式7.data 中的数据发生改变,模板中用到该数据的地方会自动更新8.在浏览器中可通过 vue.js devtools Root 中查看或临时更改 data--><div id="app"><h2>今天天气很{{info}}</h2><button @click="showInfo">切换天气</button><hr/><h3>a 的值是:{{numbers.a}}</h3><button @click="numbers.a++">点我a + 1</button></div><script lang="ts">const vm = new Vue({el: '#app',data: {isHot: false,numbers: {a:1,b:1,},},methods: {showInfo(e) {this.isHot = !this.isHot}},computed: {info () {return this.isHot ? "炎热" : "寒冷"}},watch:{// 可监视 vue 中的属性变化,也可监视计算属性中属性的变化isHot: {// immediate 为 true 时初始化时 handker 调用一次immediate:true,// handler 当 is hot 修改时调用,带两个参数:newValue:新的值,oldValue:旧的值handler(newValue, oldValue) {console.log("isHot 被修改了", newValue, oldValue)}},// 简写形式isHot(newValue, oldValue) {console.log("isHot 被修改了", newValue, oldValue)},"numbers.a": {handler(newValue, oldValue) {console.log("numbers.a 被修改了", newValue, oldValue)}},"numbers.b": {handler(newValue, oldValue) {console.log("numbers.b 被修改了", newValue, oldValue)}},numbers: {// immediate 为 true 监视多级结构中所有属性,默认为 falsedeep: true,handler(newValue, oldValue) {console.log("numbers 被修改了", newValue, oldValue)}}}})// vm.$watch('isHot', {// immediate:true,// handler(newValue, oldValue) {// console.log("isHot 被修改了", newValue, oldValue)// }// })</script>
</body>
12. 绑定样式
- 绑定 class 样式
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>初始Vue</title><script src="../js/vue.js"></script><style>.basic {width: 100px;height: 100px;}.normal {background-color: aquamarine;}.happy {background-color: hotpink;}.sad {background-color: green;}.fontStyle1 {font-size: 20px;}.fontStyle2 {font-style: italic;}.fontStyle3 {font-weight: bold;}</style>
</head>
<body> <div id="app"><div class="basic" :class="mood" @click="changeMood">{{name}}</div></br><div class="basic" :class="fontStyles">{{name}}</div></br><div class="basic" :class="classObj">{{name}}</div></br></div><script lang="ts">const vm = new Vue({el: '#app',data: {name:"hwm",mood:"normal",fontStyles:["fontStyle1", "fontStyle2", "fontStyle3", "normal"],classObj: {fontStyle1: false,fontStyle2: false,fontStyle3: false,happy: false,}},methods: {changeMood() {const moodArr = ["normal", "happy", "sad"]let idx = Math.floor(Math.random()*3)this.mood = moodArr[idx]} }})</script>
</body>
13. 条件渲染(v-show/v-if/v-else-if/v-else)
-
v-show
控制元素是否显示,设置元素 display 属性,
dom
元素依旧还在 -
v-if
控制元素是否显示,比 v-show 更彻底,
dom
元素整个添加或删除 -
v-else-if
-
v-else
-
注意点
v-if v-else-if v-else 组合使用时,元素要紧挨在一起
<div v-if="n===1">Angular</div> <div v-else-if="n===2">React</div> <div v-else-if="n===3">Vue</div> <div v-else>ming</div><!-- v-if 可配合 template 使用达到对一个组控制的效果 --> <template v-if="n===1"><div>Angular</div><div>React</div><div>Vue</div> </template>
14. v-for (列表渲染)
<body> <div id="app"><h2>人员列表:</h2><ul><li v-for="(item, index) in persons" :key="item.id">{{item.id}}-{{item.name}}-{{item.age}}<input/></li></ul><button @click.once="addPerson">添加一个老刘</button></div><script lang="ts">const vm = new Vue({el: '#app',data: {persons:[{id:"001", name:"张三", age:18},{id:"002", name:"李四", age:19},{id:"003", name:"王五", age:20},]},methods: {addPerson() {let obj = {id: "004", name: "老刘", age: 21}this.persons.unshift(obj)}}})</script>
</body>
key 值得作用:
15. 列表过滤和排序
<body> <div id="app"><h2>人员列表:</h2><input placeholder="请输入姓名" type="text" v-model="keyWord"/><button @click="sortType = 2">按年龄升序排列</button><button @click="sortType = 1">按年龄降序排列</button><button @click="sortType = 0">原序排列</button><ul><li v-for="(item, index) in filtPersons" :key="item.id">{{item.id}}-{{item.name}}-{{item.age}}-{{item.sex}}</li></ul></div><script lang="ts">const vm = new Vue({el: '#app',data: {sortType:"",keyWord:"",persons:[{id:"001", name:"马冬梅", age:22, sex:"女"},{id:"002", name:"周冬雨", age:19, sex:"女"},{id:"003", name:"周杰伦", age:20, sex:"男"},{id:"004", name:"温兆伦", age:21, sex:"男"},],},methods: {},computed: {filtPersons() {console.log("访问了filtPersons")let arr = this.persons.filter((elem => {return elem.name.indexOf(this.keyWord) !== -1}))if(this.sortType) {arr.sort((p1, p2) => {return this.sortType === 1? p2.age - p1.age : p1.age - p2.age})}return arr}},})</script>
</body>
16. set(object, key, value) 添加响应式属性
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新
注意:注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
<script lang="ts">const vm = new Vue({el: '#app',data: {car: {productionNum:"123456789",struct:{engine:"engine",tyre:"tyre",},type:[{name:"jeep", price:200},{name:"bus", price:150},{name:"moto", price:100},]}},methods: {addCarPart(event, str) {// Vue.set(this.car.struct, str, str)this.$set(this.car.struct, str, str)// 错误 this.$set(this, str, str)// 删除属性// Vue.delete(this.car.struct, str, str)this.$delete(this.car.struct, str, str)}}})
</script>
17. vue 监测数据总结
vue 会监视 data 所有层次的数据
监测对象:
通过 settter 实现监视,且要在 new vue 时就传入要监测的数据
(1)对象后追加的属性,vue 默认不做响应式处理
(2)如需给后添加的属性做响应式,以下 API
Vue.set(target, propertyName/index, value)
Vue.set(target, propertyName/index, value)监测数组:
(1) 调用原生对应的方法对数组更新(push, pop, unshift, shift, splice, sort, reverse)
(2) 重新解析魔板,更新页面
错误:this.student.friends[0] = {…}
正确:this.student.friends[0].name = “”
18. 收集表单数据示例
<body> <div id="app"><form @submit.prevent="submit">账号:<input type="text" v-model="userInfo.account"/></br>密码:<input type="password" v-model="userInfo.password"/></br></br>性别:<input type="radio" name="sex" value="male" v-model="userInfo.sex"/>男<input type="radio" name="sex" value="female" v-model="userInfo.sex"/>女</br></br>爱好:<input type="checkbox" v-model="userInfo.hobby" value="smoke"/>抽烟<input type="checkbox" v-model="userInfo.hobby" value="drink"/>喝酒<input type="checkbox" v-model="userInfo.hobby" value="perm"/>烫头</br></br>所属校区:<select v-model="userInfo.city"><option value="">请选择校区</option><option value="beijing">北京</option><option value="shanghai">上海</option><option value="shenzhen">深圳</option><option value="changsha">长沙</option></select></br></br>其它信息:<textarea v-model="userInfo.other"></textarea></br></br><input type="checkbox" v-model="userInfo.agree"/>阅读并接受<a href="https://www.baidu.com">《用户协议》</a></br></br><button>提交</button></form></div><script lang="ts">const vm = new Vue({el: '#app',data: {userInfo: {account:'',password:'',sex:'male',hobby:[],city:'changsha',other:'',agree:'',}},methods: {submit() {console.log(JSON.stringify(this.userInfo))}}})</script>
</body>
19. vue 内部指令
v-text:向其所在的节点中渲染文本内容,不支持标签文本解析
v-html:向其所在的节点中渲染文本内容,支持标签文本解析
v-cloak:没有值,vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性,配合 css 选择器使用
v-once:在初次动态渲染后,就视为静态内容(没有响应式)
v-pre:跳过所在节点的编译过程(可利用它跳过没有使用指令语法,插值语法的节点,加快编译)
20. 生命周期

21. 非单文件组件
命名规则:
错误:myCom: myCom
正确:‘myCom’: myCom
<body> <div id="app"><school></school></br><student></student></div><script lang="ts">const school = {template:`<div><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div>`,data() {return {name: "HUFE",address: "长沙",}},}const student = {template:`<div><h2>学生名称:{{name}}</h2><h2>学生年龄:{{age}}</h2></div>`,data() {return {name: "ming",age: 18,}},}const vm = new Vue({el: '#app',data: {},methods: {},components: {school, // 简写 = school:schoolstudent:student,},})</script>
</body>
22. vm 与 vc
- vm
- Vue 实例
- 通过 el 配置项决定为哪个容器服务
- data 配置项可写成对象或函数
- this 指向 vm
- vc
- VueComponent 实例
- 没有 el 配置项,可复用
- data 配置项只能写成函数
- this 指向 vc
- VueComponent.protptype.[[Prototype]]=== Vue.prototypr
23. 单文件组件(.vue)
脚手架下载:vue2 文档 -> 生态系统 -> Vue CLI
基础样式:
<template><!-- 组件结构 --><div class="demo"><hello></hello><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button></div>
</template><script>// 组件交互相关代码(数据、方法等)export default {name:'MySchool',data() {return {name: "HUFE",address: "ChangSha"}},methods: {showName() {alert(this.name)}}}
</script><style>/* 组件样式 */.demo {background-color: orange;}
</style>
使用组件:
<template><div><img src="./assets/logo.png" alt="logo"/><School></School><Student name="ming" age="18" sex="男"></Student></div>
</template><script>// 引入组件import School from "./components/School.vue";import Student from "./components/Student.vue";export default {name:'App',components: {School,Student,}}
</script><style>
</style>
24. ref 属性
-
用来给元素或子组件注册引用信息(id 的替代者)
-
应用在 html 标签上获取的是真实 DOM 元素,应用在组件标签上是组件实例对象(vc)
-
使用方式:
<!-- 打标识 --> <h1 ref="xxx"></h1> 或 <School ref="xxx"></School> <!-- 获取 --> this.$refs.xxx
25. props 属性(父传子)
props 主要用于组件的传值
props 定义的属性不要去修改
如果需要为 props 中的属性做响应式,可在 data 中声明属性并指向 props 中的属性
<!-- 组件导出 -->
<template><!-- 组件结构 --><div class="stu"><hello></hello><h2 ref="title">学生姓名:{{stuName}}</h2><h2>年龄:{{stuAge}}</h2><h2>性别:{{sex}}</h2><button @click="addAge">点我 age+1</button></div>
</template><script>// 组件交互相关代码(数据、方法等)export default {name:'MyStudent',data() {return {stuName: this.name,stuAge: this.age,}},methods: {addAge() {this.stuAge++}},// 写法一:无特殊限制,在使用组件时注意使用 v-bind(:)// props:['name', 'age', 'sex'],// 写法二:限制类型,使用组件传入值类型不对时会报错// props: {// name: String,// age: Number,// sex: String,// }// 写法三:限制类型,可选,默认值props: {name: {type: String, required: false, // 必须传的参数},age: {type: Number,default: 18, // 默认值},sex: String,}}
</script><style>/* 组件样式 */.stu {font-style: italic;}
</style><!-- 使用 -->
<Student name="ming" :age="18+1" sex="男"></Student> <!-- 使用 : 进行绑定 -->
26. mixin(混合)
提取 vc 对象中的公共属性实现在不同组件中复用
// 定义 mixin
export const h unhe = {data: {return{x: 100,y: 200,} }methods: {showName() {alert(this.name)}},mounted() {console.log("mounted")},
}// 使用 mixin
<script>import {hunhe} from ...export default {name:'',data() {return {}},mixins:[hunhe]}
</script>
27. scoped(局部样式)
<!-- 局部的样式,只在该.vue文件中使用 -->
<style scoped>
</style>
28. less
[使用手册](Less 快速入门 | Less.js 中文文档 - Less 中文网 (bootcss.com))
[安装]((22条消息) Vue 安装 Less(CSS 预处理器)_vue中如何安装less_卡尔特斯的博客-CSDN博客)
<style lang="less"></style>
29. 路由
安装router:npm install vue-router@3
基本用法:
router/index.ts:
import VueRouter from "vue-router";
import Login from '../components/Login.vue'
import Main from '../components/Main.vue'
import Register from '../components/Register.vue'export default new VueRouter({routes: [{path: '/login', // 跳转路径name: 'login', // 名称component: Login,},{path: '/main', // 跳转路径name: 'main', // 名称component: Main,},{path: '/register', // 跳转路径name: 'register', // 名称component: Register,},]
})
mian.js 中
Vue.use(VueRouter)new Vue({router,render: h => h(App),
}).$mount('#app')
单个页面中(其它类似):
<template><h2>我是Login组件</h2>
</template><script>export default {name: 'Login',}
</script>
App.vue:
<template><div id="app"><router-link to="/main">Main</router-link><br><router-link to="/login">Login</router-link><br><router-link to="/register">Register</router-link><br><router-view></router-view></div>
</template>
30. 浏览器本地存储
-
localStorage
在 Application -> Local storage 中查看
在清除浏览器缓存时会清空saveData() {localStorage.setItem("msg", "666")localStorage.setItem("person", JSON.stringify(this.p)) }, removeData() { // 清除某个缓存localStorage.removeItem("msg") }, clearData() { // 清除所有缓存localStorage.clear() }
-
sessionStorage
在 Application -> Session storage 中查看
关闭浏览器清空缓存
Api 同上,localStorage -> sessionStorage
31. $emit $on 组件自定义事件(子传父)
vm. e m i t ( e v e n t , a r g ) / / 触发当前组件上的事件, e v e n t :事件名, a r g :参数 v m . emit(event, arg) // 触发当前组件上的事件,event:事件名,arg:参数 vm. emit(event,arg)//触发当前组件上的事件,event:事件名,arg:参数vm.on(event, fn) // 监听event事件后运行 fn
- 父组件可以使用 props 把数据传给子组件
- 子组件可以使用 $emit,让父组件监听到自定义事件
-
App.vue
<template><div id="app"><h1>{{ msg }}</h1><School :getSchoolName="getSchoolName"></School><Student @stuName="getStudentName"></Student><Student ref="student"></Student></div> </template><script> import Main from './components/Main.vue'; import Student from './components/Student.vue'; import School from './components/School.vue';export default {name: 'App',data() {return {msg: "你好啊",}},methods: {getSchoolName(name) {console.log("App 收到了学校名:", name)},getStudentName(name) {console.log("App 收到了学生名:", name)},},components:{Student, School},mounted() {this.$refs.student.$on('stuName', this.getStudentName)// 只执行一次// this.$refs.student.$once('stuName', this.getStudentName)} } </script>
-
通过父组件给子组件传递函数类型的 props 实现:子给父传数据:
<template><div class="school"><h2>学校名:{{ name }}</h2><h2>学校地址:{{ address }}</h2><button @click="sendSchoolName()">把学校名给App</button></div> </template><script>export default {name: 'school',props: ['getSchoolName'],data() {return {name: 'HUFE',address: "changsha"}},methods:{sendSchoolName() {this.getSchoolName(this.name)}}} </script>
-
通过父组件给子组件绑定一个自定义事件实现:子给父传数据:
<template><div id="app"><h1>{{ msg }}</h1><School :getSchoolName="getSchoolName"></School><!-- 写法一 --><Student @stuName="getStudentName" @click.native="stuClick"></Student><!-- 写法二 --><Student ref="student"></Student></div> </template><script> import Main from './components/Main.vue'; import Student from './components/Student.vue'; import School from './components/School.vue';export default {name: 'App',data() {return {msg: "你好啊",}},methods: {getSchoolName(name) {console.log("App 收到了学校名:", name)},getStudentName(name) {console.log("App 收到了学生名:", name)},stuClick() {alert("stuClick")}},components:{Student, School},mounted() {this.$refs.student.$on('stuName', this.getStudentName)// 只执行一次// this.$refs.student.$once('stuName', this.getStudentName)} } </script>
32. nextTick()
下一次 DOM 更新结束后执行器指定回调
this.$nextTick(function(){this.$refs.inputTitle.focus() })
33. 插槽
-
默认插槽
<template><div class="sch"><h2>ming</h2><slot>默认值,组件使用者没传时显示</slot></div> </template>
使用:
<template><div class="app"><Student><img src="./assets/4.png" alt=""></Student></div> </template>
Vue 3
注意事项
导入 .vue 组件
import Child from './components/Child.vue' // 不需要带 {}
0. 创建 vue3 工程
-
通过 cli 创建 vue3 js 工程
- 查看 cli 版本,版本需在 4.5.0 以上
vue -V
npm install -g @vue/cli (默认安装最新版本cli) - 创建工程,选择 vue 版本
vue create vue3_test (工程名)
- 查看 cli 版本,版本需在 4.5.0 以上
-
通过 cli 创建 vue3 ts 工程
- 确认版本:
node(14.x 以上):node -v 若版本不够,去 node 官网下载
vue-cli(4.x 以上):vue -V 若版本不够,npm install -g @vue/cli - 创建项目
vue create vue3ts_demo(项目名)
选择第三项,自定义配置(光标移动到对应处,按空格选择)
- 确认版本:
-
通过 vite 创建 vue3 ts 工程
npm create vite@latest my-vue-app --template vue-ts
创建工程后使用:npm -i 安装模块
1. setup 函数
<template><h2>{{name}}</h2><h2>{{age}}</h2><button @click="sayHello">SayHello</button>
</template><script>export default{setup() {let name = "ming"let age = 18function sayHello() {alert(`Hello, I am ${name},my age is ${age}`)}return {name,age,sayHello,}}}
</script>
setup 语法糖
<template><h2>{{name}}</h2><h2>{{age}}</h2><button @click="sayHello">SayHello</button>
</template><script setup>let name = "ming"let age = 18function sayHello() {alert(`Hello, I am ${name},my age is ${age}`)}
</script>
2. ref 函数(为数据做响应式)
<template><h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2><h2>职位:{{ job.type }}</h2><h2>薪水:{{ job.salary }}</h2><button @click="fixName">点我修改名字</button><br><button @click="fixAge">点我修改年龄</button><br><button @click="fixJob">点我修改工作</button>
</template><script setup>import { ref } from 'vue';// ref 处理基本类型let name = ref("ming")let age = ref(18)// ref 处理对象let job = ref ({type: "前端工程师",salary: "30k",})function fixName() {name.value = "hhh"}function fixAge() {age.value = 20}function fixJob() {job.value.type = "UI工程师"job.value.salary = "50k"}
</script>
3. reactive 函数(处理对象响应式)
<template><h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2><h2>职位:{{ job.type }}</h2><h2>薪水:{{ job.salary }}</h2><h2>修改深层次对象属性:{{ job.a.b.c }}</h2><h2>爱好:{{ hobby }}</h2><button @click="fixName">点我修改名字</button><br><button @click="fixAge">点我修改年龄</button><br><button @click="fixJob">点我修改工作</button><br><button @click="fixHobby">点我修改爱好</button>
</template><script setup>import { reactive, ref } from 'vue';// ref 处理基本类型响应式let name = ref("ming")let age = ref(18)// reactive 处理对象响应式let job = reactive ({type: "前端工程师",salary: "30k",// 测试深层次a: {b: {c: 666}}})// reactive 处理对象(数组)响应式let hobby = reactive(['抽烟', '喝酒', '烫头'])function fixName() {name.value = "hhh"}function fixAge() {age.value = 20}function fixJob() {job.type = "UI工程师"job.salary = "50k"job.a.b.c = 999}function fixHobby() {hobby[0] = '学习'}
</script>
4. 新增/删除属性
<template><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>性别:{{ person.sex }}</h2><button @click="addInfo">添加信息</button><br><button @click="delInfo">删除信息</button><br>
</template><script setup>import { reactive, } from 'vue';let person = reactive({name: "ming",age: 18})function addInfo() {person.sex = "男"}function delInfo() {delete person.name}
</script>
5. computed 计算属性
<template>姓:<input type="text" v-model="person.firstName"/><br>名:<input type="text" v-model="person.lastName"/><br>名:<input type="text" v-model="person.fullName"/><br>
</template><script setup>import { reactive, computed} from 'vue';let person = reactive({firstName: "ming",lastName: "ming",})// 简写,没有 setter// let fullName = computed(() => {// return person.firstName + '-' + person.lastName// })// 完整写法person.fullName = computed({get() {return person.firstName + '-' + person.lastName},set(value) {const nameArr = value.split('-')person.firstName = nameArr[0]person.lastName = nameArr[1]}})
</script>
6. watch 数据监视
<template><h2>num:{{ num }}</h2><button @click="num++">点我num+1</button><br><h2>num:{{ str }}</h2><button @click="str+='!'">点我str+!</button><br><h2>姓名{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2> <h2>薪资:{{ person.job.salary }}</h2> <button @click="person.name+='~'">点我修改姓名</button><button @click="person.age++">点我修改年龄</button><button @click="person.job.salary++">点我修改薪资</button>
</template><script setup>import {ref, reactive, computed, watch} from 'vue';let num = ref(0)let str = ref("Hello")let person = reactive({name: "ming",age: 18,job: {type: "前端工程师",salary: "20",}})// 情况一:监视一个 ref 所定义的响应式数据// watch(num, (newV, oldV) => {// console.log(`newV${newV},old:${oldV}`)// }, {immediate: true})// 情况二:监视多个 ref 所定义的响应式数据 newV:[newNum, newStr] oldV:[oldNum, oldStr]// watch([num, str], (newV, oldV) => {// console.log(newV,oldV)// })/* 情况三:监视 reactive 所定义的一个响应式数据,踩坑点:1. 无法正确获取 oldValue2. 强制开启了深度监视(deep 配置无效)*/// watch(person, (newV) => {// console.log('person 变化了', newV)// })// 情况四:监视 reactive 所定义的一个响应式数据的某个属性watch(() => person.age, (newV, oldV) => {console.log('person.age 变化了', newV, oldV)})</script>
7. watchEffect
/* 1.初始就会执行一次2.监视函数体中使用到某个属性,若该属性改变了就会执行一次*/watchEffect(() => {console.log("watchEffect 调用了")let x = num.valuelet y = person.job.salary})
8. 生命周期
图:官网查看
beforeCreate == setup()
beforeCreate == setup()
9. hook (复用代码)
创建一个 usePoints.js 代码
import { onBeforeUnmount, onMounted, reactive } from "vue";export default function() {// 实现竖版“打点”相关数据let point = reactive({x:0,y:0,})// 实现鼠标“打点”相关方法function savePoint(event) {point.x = event.pageXpoint.y = event.pageYconsole.log(event.pageX, event.pageY)}// 实现鼠标“打点”相关的生命周期狗子onMounted(() => {window.addEventListener('click', savePoint)})onBeforeUnmount(() => {window.removeEventListener('click', savePoint)})return point
}
在组件中复用:
<template><!-- 组件结构 --><div class="demo"><h2>鼠标点击X坐标:{{ point.x }}</h2><h2>鼠标点击Y坐标:{{ point.y }}</h2></div>
</template><script setup>import usePoint from '../hook/usePoints'let point = usePoint()
</script><style scoped>/* 组件样式 */.demo {background-color: orange;}
</style>
10. toRef 和 toRefs
- toRef: 复制 reactive 里的单个属性并转成 ref
- toRefs: 复制 reactive 里的所有属性并转成 ref
<template><h2>reactive-greet: {{ info.greet }} </h2><h2>toRef-greet: {{ rGreet }}</h2><button @click="onChangeGreet">更换问候语</button>
</template><script>
import { reactive, toRef } from 'vue'
export default {setup() {let info = reactive({name: 'Tony',greet: 'Hello'})// 复制整个 infolet rInfo = toRefs(info)// 复制 info 里的 greet 属性let rGreet = toRef(info, 'greet')// 更改 rGreetconst onChangeGreet = () => {rGreet.value = 'world!'}return {info,rGreet,onChangeGreet}}
}
</script>
11. shallowReactive
只处理对象最外层属性的响应式(浅响应式)
12. readonly 和 shallowReadOnly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadOnly: 让一个响应式数据变为只读的(浅只读)
13. toRaw 和 markRaw
- toRaw : 将一个 reactive 生成的响应式对象转为普通对象
- markRaw: 标记一个对象,使其永远不会再成为响应式对象
14. provide 和 inject
作用:实现祖孙间通信
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QXKRhQah-1689749017405)(C:\Users\cli08\AppData\Roaming\Typora\typora-user-images\image-20230530234850863.png)]
<!-- app -->
<script setup>let car = {name: "奔驰",price: "70w"}provide('car', car)components:{Child}
</script><!-- 子组件 -->
<script setup>let car = inject('car')components:{Son}
</script><!-- 孙组件 -->
<template><div class="son"><h3>我是孙组件 {{ car.name }}---{{ car.price }}</h3></div>
</template><script setup>import { inject } from 'vue';let car = inject('car')
</script>
15.响应式数据判断
isRef
,是否是由ref
定义的响应式数据isReactive
,是否是由reactive
定义的响应式数据isReadonly
,是否是由readonly
定义的数据isProxy
,是否是由reactive
或readonly
定义的数据
16. Router (路由)
- 安装:npm install vue-router@4
- useRouter:对路由的控制,用来路由跳转
useRoute:路由的信息,用来路由传参
-
使用示例
单个页面(Login.vue 和 Main.vue):
<template><h2>我是Login组件</h2> </template><script setup></script>
Game.vue
<template><div class="game"><h2>我是Game组件</h2><router-link to="/game/game_1">Game_1</router-link><br><router-link to="/game/game_2">Game_2</router-link><br><router-link to="/game/game_3">Game_3</router-link><br><router-view></router-view></div> </template><script setup lang="ts"></script><style scoped>.game {background-color: pink;padding: 10px;} </style>
Game_1.vue(Game_2.vue, Game_3.vue 类似)
<template><div class="game_1"><h2>我是Game_1组件</h2></div> </template><script setup lang="ts"> </script><style scoped>.game_1 {background-color: goldenrod;} </style>
main.js
import { createApp } from 'vue' import App from './App.vue' import router from './router/index.ts'createApp(App).use(router).mount('#app')
router/index.ts
// history模式 import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router'const routes : RouteRecordRaw[] = [{path: '/login', // 跳转路径name: 'login', // 名称component: () => import('../components/Login.vue'),},{path: '/main', // 跳转路径name: 'main', // 名称component: () => import('../components/Main.vue'),},{path: '/game', // 跳转路径name: 'game', // 名称component: () => import('../components/Game.vue'),children:[{path: 'game_1',name: 'game_1',component: () => import('../components/Game_1.vue'),},{path: 'game_2',name: 'game_2',component: () => import('../components/Game_2.vue'),},{path: 'game_3',name: 'game_3',component: () => import('../components/Game_3.vue'),},]}, ]// 创建路由对象 const router = createRouter({history: createWebHashHistory(),routes, })export default router
App.vue
<template><router-link to="/main">Main</router-link><br><router-link to="/login">Login</router-link><br><router-link to="/register">Register</router-link><br><router-view></router-view> </template><script>export default {name: 'App',} </script>
-
路由传参
-
query
<router-link :to="{path: '/main',query: {p1: 'ming',p2: 18} }">Main </router-link><br><!-- 在对应组件上接收参数 --> <script setup lang="ts">import { useRouter, useRoute } from 'vue-router';const route = useRoute()console.log(route.query) </script>
-
param
<router-link :to="{name: 'login',params: {p3: 'ming',p4: 18} }">Login</router-link><br><!-- 在对应组件上接收参数 --> <script setup lang="ts">import { useRoute, useRouter } from 'vue-router';const route = useRoute()console.log(route.params) </script>
-
-
编程式路由导航
const router = useRouter() // 跳转页面 router.push('path', query: {}) // 压入历史记录栈顶 router.push(name: 'name', param: {}) // 压入历史记录栈顶 router.replace('path', query: {}) // 替换历史记录栈顶 router.replace(name: 'name', param: {}) // 替换历史记录栈顶 router.go(1) // 向前移动一条历史记录 router.go(-1) // 向后移动一条历史记录
17. pinia 状态管理器使用
安装 pinia :npm install pinia
修改main.js,引入pinia提供的createPinia方法,创建根存储。
// main.tsimport { createApp } from "vue"; import App from "./App.vue"; import { createPinia } from "pinia"; const pinia = createPinia();const app = createApp(App); app.use(pinia); app.mount("#app");
src 下创建文件夹 store,用来存放共享数据
-
创建 store
import { defineStore } from 'pinia' import { reactive } from 'vue';interface Student {name: string,age: number,sex: string,job: {type: string,salary: string,} }export const useUsersStore = defineStore("users", () => {const student = reactive<Student>({name: 'ming',age: 18,sex: '男',job: {type: '前端工程师',salary: '30k',}})const addAge = (add: number) => {student.age += add}return {student,addAge};});
-
使用
<template><img alt="Vue logo" src="./assets/4.png" /><h2>姓名:{{ student.name }}</h2><h2>年龄:{{ student.age }}</h2><h2>性别:{{ student.sex }}</h2><h2>职位:{{ student.job.type }}</h2><h2>薪水:{{ student.job.salary }}</h2><button @click="addAge(1)">点我年龄+1</button> </template><script setup lang="ts">import { ref, reactive } from "vue";import { useUsersStore } from "../src/store/user";const userStore = useUsersStore();const {student, addAge} = userStoreconsole.log(userStore) </script><style scoped> </style>
18. ref 获取 DOM 元素
<script setup lang="ts">const ctx = ref<HTMLDivElement>()onMounted(() => {console.log("hwm", ctx.value)})
</script><template><div class="game-list" ref="ctx"></div>
</template>