Vue: 事件修饰符, 键盘事件, 鼠标事件,计算属性
目录
事件修饰符
阻止默认事件
阻止冒泡
允许触发一次
捕获模式
self
passive
键盘事件
keyup & keydown
按键别名
注意tab
注意系统按键
自定义按键
鼠标事件
简介
鼠标焦点事件
计算属性
差值语法实现
methods实现
computed实现
get()
set()
总结
计算属性简写
事件修饰符
使用v-on, 表示即将发生的事件, 例如点击事件click, 下面是一个例子:
<div id="app"><h5 v-on:click="showInfo">点击我</h5><a href="https://www.baidu.com" v-on:click="showInfo">点我</a></div>
v-on 可以简写为: @
<div id="app"><h5 @click="showInfo">点击我</h5><a href="https://www.baidu.com" @click="showInfo">点我</a></div>
阻止默认事件
点击a标签, 会执行click中的showInfo方法, 随后还会执行a中的默认跳转行为, 如果不想这类行为事件发生, 可以使用事件修饰符prevent来阻止进行:
<a href="https://www.baidu.com" @click.prevent="showInfo">点我</a>
当然可以在Vue实例中定义方法的时候, 传入event, 然后调用event的preventDefault方法:
new Vue({el : '#app',data : {name : '王者荣耀'},methods : {showInfo(e) {e.preventDefault()alert('你好')}}})
阻止冒泡
既然是点击, 那么如果一个区域里面存在两个点击事件. 例如:
<body><div id="app"><div @click="testDiv" id="out"><a href="https://www.baidu.com" @click.prevent="showInfo">点我</a></div></div><style>#out {width: 200px;height: 200px;border: solid blue 1px;}</style>
</body>
<script>new Vue({el : '#app',data : {name : '王者荣耀'},methods : {showInfo(e) {e.preventDefault()alert('你好')},testDiv() {alert('这是最外层的div')}}})
</script>
点击a标签那么就会出现:
首先出现:
然后出现:
发现, 本来我的本意只想点击这个a标签, 但是因为层叠关系(冒泡), 就导致点击了a的同时, 也点击了div标签, 所以为了阻止这种情况, 使用event方法: event.stopPropagation() >>>
new Vue({el : '#app',data : {name : '王者荣耀'},methods : {showInfo(e) {e.preventDefault()e.stopPropagation();alert('你好')},testDiv(e) {alert('这是最外层的div')}}})
简写, 在事件后面添加事件修饰符stop, 例如:
<a href="https://www.baidu.com" @click.prevent.stop="showInfo">点我</a>
允许触发一次
使用修饰符once :
<a href="https://www.baidu.com" @click.once="showInfo">点我</a>
事件只触发一次
捕获模式
事件执行, 先开始捕获, 由外到内, 捕获完之后开始执行, 从内到外执行, 也就是冒泡, 现在更改为捕获状态就执行, 也就是添加.capture, 例如@click.capture = "xxx"
self
@click.self, 只有在event.target 是本身的时候, 才会触发点击事件
passive
事件立即执行, 无序等待前置条件: .passive
键盘事件
keyup & keydown
- @keyup
- @keydown
这里使用keyup作为案例:
<body><div id="app"><p>欢迎来到{{name}}</p><input type="text" placeholder="按下回车提示输入" @keyup="showInfo"></div>
</body><script>new Vue({el :'#app',data : {name : 'world'},methods : {showInfo(e) {console.log(e.target.value);}}})
</script>

按下a建., 输入一个a, 这个时候控制台还没有输入, 但是松开a键之后, 控制台输出:

再按下一个a, 松开之后, 控制台输出 两个a :

keydown的用法类似, 这里不一一列举
相同的keyup和keydown都有事件修饰符:
- enter, 键盘输入之后并不会触发事件, 只有在键入的值是enter(回车)的时候, 才会触发事件, 例如:
<div id="app"><p>欢迎来到{{name}}</p><input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"></div>new Vue({el :'#app',data : {name : 'world'},methods : {showInfo(e) {console.log(e.target.value);}}})输入一个a并不会触发事件showInfo, 但是在输入a后,再按一个回车, 控制台就输出了一个a :

按键别名
-
相同的还有很多其他的键盘事件修饰符, 这些别的修饰符是我们vue中常用的按键的别名:
-
回车 : enter
-
删除 : delete
-
空格 : space
-
退出 : esc
-
换行 : tab
-
上 : up
-
下 : down
-
左 : left
-
右 : right
-
vue暂时没有提供别的按键别名, 可以使用按键原始的key进行绑定
系统按键修饰符:
- ctrl
- alt
- shift
- meta (win)
系统修饰键配合keyup使用, 按下修饰键的同时, 再按下其他键, 随后释放其他键, 事件才被触发 : 解释 >> 因为同时按下之后, keyup是属于松开才触发事件, 但是现在除了ctrl等键之外, 其他的一些按键同样被按下了, 那么他们之间就有一个释放的顺序, 所以现在只有其他键先释放, 这个keyup状态才结束, 事件执行
配合keydown使用, 直接按下之后就可以正常触发事件
注意tab
有下面的例子:
<body><div id="app"><p>欢迎来到{{name}}</p><input type="text" placeholder="按下回车提示输入" @keyup.tab="showInfo"></div>
</body><script>new Vue({el :'#app',data : {name : 'world'},methods : {showInfo(e) {console.log(e.target.value);}}})
</script>
这是一个使用键盘事件, 并且使用tab修饰这个键盘事件 :

我们输入aaa, 然后按下tab:

发现这个玩意没有输出到控制台:

多按几次就会发现, 他并不是没有作用, 反而像是被什么锁定了一样, 不能在input中输入, :

多按下几次就会发现, 他会在浏览器上切换焦点
这个焦点被切走了. 那为什么没有触发事件??
分析: 细心的你一定会发现, 这个tab是按下立即就生效的, 但是我们定义的键盘事件是keyup, 按下tab之后, 焦点立即调走, 还没来得及松开焦点就已经从输入框中切走, 那么vm也就无法捕捉到这个键入tab的行为, 更加捕获不到tab松开.
改进 : 所以这个时候, 我们就可以使用keydown事件
注意系统按键
同按键别名中的说明, 如果直接使用@keyup.ctrl .....
比如同时按下ctrl + y, 然后松开y之后, 触发事件
还有一个需要注意的是, 你可以指定松开哪一个键执行事件:
@keyup.ctrl.y, 这个时候, 只有同时按下ctrl + y, 然后松开y才有用, 按下ctrl + z或者是其他的除y之外的按键都是没有用的.
自定义按键
Vue.config.keyCodes.huiche = 13;<input type="text" placeholder="按下回车提示输入" @keyup.huihce="showInfo">
鼠标事件
简介
除了键盘事件, 那么肯定有鼠标事件的啦, 看看下面这几个鼠标事件的例子:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../js/vue.js"></script>
</head>
<body><div id="app"><div id="testDiv" @click="testClick" @dblclick="testDBClick" @mouseenter="testMouseEnter" @mouseleave="testMouseLeave" @mousedown="testMouseDown"@mouseup="testMouseUp"@mousemove="testMouseMove">{{content}}</div> <br><hr></div>
</body>
<script>var vue = new Vue({el : '#app',data : {content: '',value : null},methods : {testClick() { // 单机this.content = '鼠标单击';},testDBClick() { // 鼠标双击this.content = '鼠标双击';},testMouseEnter() { // 鼠标进入事件this.content = '鼠标进入';},testMouseLeave() { // 鼠标离开事件this.content = '鼠标离开';}, testMouseDown() { // 鼠标按下事件this.content = '鼠标按下';}, testMouseUp() {this.content = '鼠标弹起';}, testMouseMove(event) { // 任何事件都可以添加事件event对象this.content = event.offsetX + '---' + event.offsetY; // 只要是@的事件都可以使用event}, testFocus() {this.value ='获得焦点'}, testBlur() {this.value = '失去焦点'}}});
</script>
<style>#testDiv {width: 150px;height: 150px;border: 2px solid green;text-align: center;line-height: 100%;}
</style>
</html>
单击

双击

离开div

这里不一一展示
总结鼠标点击事件 :
| 鼠标事件 | 所表示触发的前置条件 |
|---|---|
| @click | 鼠标单机触发 |
| @dblclick | 鼠标双击 |
| @mouseenter | 鼠标进入 |
| @mouseleave | 鼠标离开 |
| @mousedown | 鼠标按下 |
| @mouseup | 鼠标弹起 |
| @mousemove | 鼠标移动 |
鼠标焦点事件
- @focus : 鼠标聚焦目标以触发事件
- @blur : 鼠标聚焦跳出目标, 触发事件
案例:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../js/vue.js"></script>
</head>
<body><div id="app"><input type="text" @focus="testFocus" @blur="testBlur" v-model="value"></div>
</body>
<script>var vue = new Vue({el : '#app',data : {content: '',value : null},methods : { testFocus() {this.value ='获得焦点'}, testBlur() {this.value = '失去焦点'}}});
</script>
</html>
首先不进行任何操作:

点击这个输入框, 触发聚焦事件 :

鼠标点击输入框之外的内容:

单词解释:

blur也就是鼠标模糊不清, 也就是失去焦点.
计算属性
差值语法实现
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../js/vue.js"></script>
</head>
<body><div id="root">姓 : <input type="text" v-model="firstName"><br>名 : <input type="text" v-model="lastName"> <br>姓名 : <span>{{firstName}}{{lastName}}</span></div>
</body>
<script>new Vue({el :'#root',data : {firstName : '张',lastName : '三'}})
</script>
</html>

methods实现
{{}}的里面写的是差值表达式, 所以这里直接使用方法名 + ()的形式, 表示调用这个函数的返回值
<body><div id="root">姓 : <input type="text" v-model="firstName"><br>名 : <input type="text" v-model="lastName"> <br>姓名 : <span>{{fullName()}}</span></div>
</body>
<script>new Vue({el: '#root',data: {firstName: '张',lastName: '三'},methods :{fullName() {return '博子妹妹';}}})
</script>

这里将'博子妹妹' 修改为 this.firstName + '-' + lastName的形式, 即可联动显示, 因为是双向绑定, 修改 姓 之后, 就会修改data中的值, 然后vm捕捉到这个修改, 然后用到这个name的地方就会更新 . 以后任何修改data的数据操作之后, vm都会重新解析一次.
computed实现
get()
不用方法, 也不使用插值语法来表示, 那么就使用vm实例中的computed字段来实现,
computed, 本就有计算的意思:

在computed的字段中定义fullName, 然后使用这个fullName去计算出 全名, 使用的时候, 可以像data中的数据那样去访问这个fullName, 例如: {{fullName}}, 访问这个fullName, vm会调用computed中的fullName的get()方法, 然后将其返回值最为fullName的值.
new Vue({el: '#root',data: {firstName: '张',lastName: '三'},computed : {fullName : { // 这个fullName依然在vm身上// get() : 当有人读取fullName的时候, 就会调用get方法, 返回值就会被当做fullName的值get() {return this.firstName + this.lastName;}}}})
但是这个get方法中不能直接 return firstName + lastName,因为他访问不到data中的数据,但是可以通过vm实例对象来访问.
<body><div id="root">姓 : <input type="text" v-model="firstName"><br>名 : <input type="text" v-model="lastName"> <br>姓名 : <span>{{fullName}}</span></div>
</body>
<script>new Vue({el: '#root',data: {firstName: '张',lastName: '三'},computed : {fullName : { // 这个fullName依然在vm身上// get() : 当有人读取fullName的时候, 就会调用get方法, 返回值就会被当做fullName的值get() {return this.firstName + this.lastName;}}}})
</script>

思考> computed中的属性的get 会被调用??
解释: 初次读取computed中的属性的时候,对应的get方法会被调用, 由于调用之后, 其返回值被缓存, 下次读取就不会调用get方法, 其次就是所依赖的数据被修改的时候, 就上面例子而言, 如果输入框中的姓被修改的时候, get方法就会被重新调用.
fullName虽然是个对象, 但是不能直接使用: {{fullName.get()}}.
set()
有get, 那么必然有set() :
computed : {fullName : { // 这个fullName依然在vm身上// get() : 当有人读取fullName的时候, 就会调用get方法, 返回值就会被当做fullName的值get() {return this.firstName + this.lastName;},set (value) {const arr = value.split('-');this.firstName = arr[0];this.lastName = arr[1];}}}
我们讲到, 如果调用了set方法, 那么就会修改firstName和lastName属性, 那么vm就会捕获到这个修改, 从而重新更新使用到这两个属性的地方, 同时set方法也捕获到了这个修改, 然后将引用这个fullName的地方进行更新:


总结
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
如果计算属性中存在在vm外部定义的变量, 那么确实是可以像已有的属性那样显示出来, 但是vm无法捕捉到这个外部定义的变量的变化.
2.原理: 底层借助了objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制 (复用) ,效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
计算属性简写
计算属性中的每一个属性都是当做对象处理:
对象中的每个方法使用逗号分割, 但是实际上这个set方法一般都用不到, 所以直接将这个fullName定义为一个方法 :
const vm = new Vue({el: '#root',data: {firstName: '张',lastName: '三'},computed : {fullName : function(){ // 这个fullName依然在vm身上return this.firstName + this.lastName;}}})
更甚将其改为:
fullName(){ // 这个fullName依然在vm身上return this.firstName + this.lastName;}

注意这里简写的前置条件是, 我不会去调用set方法, 如果调用set方法:

[vue警告]:计算属性, “FulName”分配给了它,但它没有set方法.

相关文章:
Vue: 事件修饰符, 键盘事件, 鼠标事件,计算属性
目录 事件修饰符 阻止默认事件 阻止冒泡 允许触发一次 捕获模式 self passive 键盘事件 keyup & keydown 按键别名 注意tab 注意系统按键 自定义按键 鼠标事件 简介 鼠标焦点事件 计算属性 差值语法实现 methods实现 computed实现 get() set() 总…...
【ROS2】MOMO的鱼香ROS2(四)ROS2入门篇——ROS2节点通信之话题与服务
ROS2节点通信之话题与服务点 引言1 理解从通信开始1.1 TCP(传输控制协议)1.2 UDP(用户数据报协议)1.3 基于共享内存的IPC方式 2 ROS2话题2.1 ROS2话题指令2.2 话题之RCLPY实现2.2.1 编写发布者2.2 2 编写订阅者2.2.3 运行测试 3 R…...
2022年山东省职业院校技能大赛高职组云计算赛项试卷第三场-公有云
2022年山东省职业院校技能大赛高职组云计算赛项试卷 目录 2022年职业院校技能大赛高职组云计算赛项试卷 【赛程名称】云计算赛项第三场-公有云 【任务1】公有云服务搭建[10分] 【适用平台】华为云 【题目1】私有网络管理[0.5分] 【题目2】云实例管理[0.5分] 【题目3】数…...
现代 NLP:详细概述,第 1 部分:transformer
阿比吉特罗伊 一、说明...
记一次Mac端mysql重置密码
在执行mysql命令的时候,报如下的错误,表示不支持mysql命令: zsh: command not found: mysql 1. 先查看mysql服务是否存在 在系统偏好设置中查看: 2. 发现mysql服务已经在运行,可能因为/usr/local/bin目录下缺失mysq…...
【开题报告】基于java的流浪之家动物领养网站的设计与开发
1.选题背景 流浪之家动物领养网站的设计与开发背景主要源于对流浪动物保护和宠物领养问题的关注。随着城市化进程加快,越来越多的流浪动物出现在城市中,它们面临着食物、住所和医疗资源的缺乏。同时,许多爱心人士希望能够给流浪动物一个温暖…...
训狗技术从初级到高级,专业有效的训狗训犬教程
一、教程描述 现在大部分人家里都会养些宠物,比如狗狗,虽然狗狗的一些行为习惯跟遗传有关,但是主人后天的影响也会给狗狗带来改变,本套教程教你纠正狗狗的不良行为,可以让你与狗愉快地玩耍。本套训狗教程,…...
如何让机器人具备实时、多模态的触觉感知能力?
人类能够直观地感知和理解复杂的触觉信息,是因为分布在指尖皮肤的皮肤感受器同时接收到不同的触觉刺激,并将触觉信号立即传输到大脑。尽管许多研究小组试图模仿人类皮肤的结构和功能,但在一个系统内实现类似人类的触觉感知过程仍然是一个挑战…...
datax
DataX DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、SQL Server、Oracle、PostgreSQL、HDFS、Hive、HBase、OTS、ODPS 等各种异构数据源之间高效的数据同步功能。 https://github.com/alibaba/DataX Features DataX本身作为数据同…...
【Java】SpringBoot快速整合WebSocket实现客户端服务端相互推送信息
目录 什么是webSocket? webSocket可以用来做什么? WebSocket操作类 一:测试客户端向服务端推送消息 1.启动SpringBoot项目 2.打开网站 3.进行测试消息推送 4.后端进行查看测试结果 二:测试服务端向客户端推送消息 1.接口代码 2.使…...
C语言 linux文件操作(一)
文章目录 一、linux文件权限1.1文件描述符1.2文件描述符的范围和默认值1.3打开文件和文件描述符1.4标准文件描述符1.5文件描述符的重定向和关闭1.6I/O 操作1.7使用文件描述符进行进程通信1.8资源限制 二、C语言文件读写2.1open 函数2.2 flags参数详解2.3 lseek 函数 一、linux文…...
007、控制流
先看下本篇学习内容: 通过条件来执行 或 重复执行某些代码 是大部分编程语言的基础组成部分。在Rust中用来控制程序执行流的结构主要就是 if表达式 与 循环表达式。 1. if表达式 if表达式允许我们根据条件执行不同的代码分支。我们提供一个条件,并且做出…...
将学习自动化测试时的医药管理信息系统项目用idea运行
将学习自动化测试时的医药管理信息系统项目用idea运行 背景 学习自动化测试的时候老师的运行方式是把医药管理信息系统项目打包成war包后再放到tomcat的webapp中去运行,于是我想着用idea运行会方便点,现在记录下步骤方便以后查找最开始没有查阅资料&am…...
k8s 的YAML文件详解
一、yaml文件简介 Kubernetes只支持YAML和JSON格式创建资源对象,JSON格式用于接口之间消息的传递,适用于开发;YAML格式用于配置和管理,适用于云平台管理,YAML是一种简洁的非标记性语言。 1)yaml的语法规则&…...
【Pytorch】Pytorch或者CUDA版本不符合问题解决与分析
NVIDIA CUDA Toolkit Release Notes Package installation issues INSTALL PYTORCH 先声毒人:最好资料就是上面三份资料,可以通过官网明确的获取一手信息,你所遇到的99%的问题都可以找到,明确的解决方案,建议最好看…...
『精』CSS 小技巧之BEM规范
『精』CSS 小技巧之BEM规范 文章目录 『精』CSS 小技巧之BEM规范一、什么是BEM?二、BEM要怎么用?三、不用BEM会少个胳膊吗?💊四、Sass与BEM的结合🎈五、块与修饰符应放在一块👿参考资料💘推荐博…...
vue3-12
需求是用户如果登录了,可以访问主页,如果没有登录,则不能访问主页,随后跳转到登录界面,让用户登录 实现思路,在用户登录之前做一个检查,如果登录了,则token是存在的,则放…...
操作系统期末复习
分段存储管理方式 某采用段式存储管理的系统为装入主存的一个作业建立了如下段表: 段号 段长 主存起始地址 0 660 210 1 140 3300 2 100 90 3 580 1237 4 960 1959 (1)计算该作业访问[0,432],[1&am…...
element el-table实现可进行横向拖拽滚动
【问题】表格横向太长,表格横向滚动条位于最底部,需将页面滚动至最底部才可左右拖动表格,用户体验感不好 【需求】基于elment的el-table组件生成的表格,使其可以横向拖拽滚动 【实现】灵感来源于这篇文章【Vue】表格可拖拽滚动&am…...
【兔子王赠书第14期】《YOLO目标检测》涵盖众多目标检测框架,附赠源代码和全书彩图!
文章目录 写在前面YOLO目标检测推荐图书本书特色内容简介作者简介 推荐理由粉丝福利写在后面 写在前面 小伙伴们好久不见吖,本期博主给大家推荐一本关于YOLO目标检测的图书,该书侧重目标检测的基础知识,包含丰富的实践内容,是目标…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
