探索vue2框架的世界:简述常用的vue2选项式API (一)
组合选项
template模板
- template => 组件的视图层 只能向外暴露一个元素
<template>
<div>
{{a}} --- {{b}}
</div>
<div>
123
</div>
</template>
- 以上这个写法暴露的两个div,所以会报错
script逻辑层
provide 用于提供可以被后代组件注入的值
- 类型
interface ComponentOptions {
provide?: object | ((this: ComponentPublicInstance) => object)
}
-
provide
和inject
通常成对一起使用,使一个祖先组件作为其后代组件的依赖注入方,无论这个组件的层级有多深都可以注入成功,只要他们处于同一条组件链上。 -
这个
provide
选项应当是一个对象或是返回一个对象的函数。这个对象包含了可注入其后代组件的属性。你可以在这个对象中使用 Symbol 类型的值作为 key。 -
基本使用方式
const s = Symbol()
export default {
provide: {
foo: 'foo',
[s]: 'bar'
}
}
- 使用函数可以提供其组件中的状态
export default {
data() {
return {
msg: 'foo'
}
}
provide() {
return {
//所供给的 msg 将不会是响应式的
msg: this.msg
}
}
}
inject 用于声明要通过从上层提供方匹配并注入进当前组件的属性
- 类型
interface ComponentOptions {
inject?: ArrayInjectOptions | ObjectInjectOptions
}
type ArrayInjectOptions = string[]
type ObjectInjectOptions = {
[key: string | symbol]:
| string
| symbol
| { from?: string | symbol; default?: any }
}
- inject 选项应该是以下两种之一:
-
一个字符串数组
-
一个对象,其key名就是在当前组件中的本地绑定名称,而它的值应该是以下两种之一
- 它的 from 属性是一个 key (string 或者 Symbol),用于匹配可用的注入
- 它的 default 属性用作候补值。和 props 的默认值类似,如果它是一个对象,那么应该使用一个工厂函数来创建,以避免多个组件共享同一个对象。
-
如果没有供给相匹配的属性、也没有提供默认值,那么注入的属性将为 undefined。
-
请注意,注入绑定并非响应式的。这是有意为之的一个设计。如果要注入的值是一个响应式对象,那么这个对象上的属性将会保留响应性。
-
基本使用方式
export default{ inject:['foo'], created(){ console.log(this.foo) } }
-
使用注入的值作为props的默认值
const child= { inject: ['foo'], props: { bar:{ default(){ return this.foo } } } }
-
使用注入的值作为data
const Child={ inject:['foo'], data(){ return{ bar:this.foo } } }
-
注入项可以选择是否带有默认值
const Child={ inject:{ foo:{default:'foo'} } }
-
如果需要从不同名字的属性中注入,请使用 from 指明来源
const Child={ inject:{ foo:{ from:'bar', default:'foo' } } }
-
和props 默认值类似,对于非原始数据类型的值,你需要使用使用工厂函数
const Child = { inject: { foo: { from: 'bar', default: () => [1, 2, 3] } } }
-
name:“” 声明当前组件的名字
<script>
export default {
name:'export',
data(){
return{
....
}
}
......
}
</script>
data(){} 数据属性
- Vue实例的数据对象,vue将会递归将data的属性转换成getter/setter,从而让data的属性能够响应数据变化。对象必须是纯粹的对象(含有0个或多个key/value对)
- 注意:不应该对data属性使用箭头函数,例如data:()=>{
return{a:this.myProp}}。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向Vue实例,this.myProp将是undefined
存储在data里的属性,会自动的拥有监听器Observer,当发生变化时,会自动更新页面的数据
<script>
export default{
name:"Hellovue",
//data一定要使用闭包的形式,所以用函数形式而不用对象形式data:{}
data(){
return{
}
}
}
</script>
this.$set(this.obj,”a”,2) //给响应式对象动态添加属性
this.$delete(this.obj,”a”)//给响应式对象动态删除属性
this.util.defineRective(this.obj,”a”,2)//给非响应式对象动态添加属性
<h3 ref=“h3“>哈哈哈</h3> //ref是一个dom的标识符
this.$refs.h3 //vue里面获取dom元素
扩展:
为什么data是一个函数?
- vue文件是组件,存在复用特征,重复使用组件时,每次使用都会触发data函数,返回一个新的对象类型,多个复用的组件,数据互不影响。
- 因为最后编译打包上线的时候,是把所有的vue文件打包到一起去的,而且在脚手架中data使用对象形式会报错,所以应该使用函数得形式,实则是在使用闭包,当重复使用使用组件时,数据互相不影响。
Vue2.0中如何检测数组变化
- vue的Observer对数组做了单独的处理,对数组方法进行编译,并赋值给数组属性的proto属性上,因为原型链的机制,找到对应的方法就不会继续往上找了。编译方法时会对一些会增加索引的方法(push unshift splice)进行手动的observe
methods:{} 方法属性
- methods的方法是操作data里的变量,new Vue会一上来把methods打散,方法直接隶属于new vue,所以方法可以用this访问平级的变量
- methods将混入到vue实例中,可以直接通过VM实例访问这些方法,或者在指令表达式中使用。方法中的this自动绑定为vue实例
var vm=new Vue({
data:{a:1}
methods:{
plus:function(){
this.a++
}
}
})
vm.plus()
vm.a //2
components:{} 注册子组件
- 引入子组件
import BasicChild1 from '../components/Basic/BasicChild1.vue'
- 注册子组件
components:{
BasicChild1
}
- 使用子组件
<basic-child1></basic-child1>
注: 全局注册组件Vue.component可以直接在组件中使用;子组件的作用是用来复用,去耦合的。
computed:{} 方法属性
- Vue中的computed实现原理
- 当组件初始化的时候,computed和data会分别创建各自的响应系统,Observer遍历data中每个属性设置get/set拦截器
- 初始化computed会调用initComputed函数
- 注册一个watcher实例,并在内实例化一个Dep消息订阅器用作后续收集依赖(比如渲染函数的watcher或者其他观察该计算属性变化的watcher)
- 调用计算属性时会触发其Object.defineProperty的get访问器函数
- 调用watch.depend()方法向自身的消息订阅器dep的subs中添加其他属性的watcher
- 调用watcher的evaluate方法(进而调用watcher的get方法)让自身成为其他watcher的消息订阅器的订阅者,首先将watcher赋值给Dep.target,然后执行getter求值函数,当访问求值函数里面的属性(比如data,props或其他computed)时,会同样触发他们的get访问器函数从而将该计算属性的watcher添加到求值函数中属性的watcher的消息订阅器dep中当这些操作完成,最后关闭dep,target赋为null并返回求值函数结果。
- 当某个属性发生变化,触发set拦截函数,然后调用自身消息订阅器dep的notify方法,遍历当前dep中保存着所有订阅者watcher的subs数组并逐个调用watcher的update方法,完成响应更新。
-
计算属性被混入到Vue实例中。所有getter和setter的this上下文自动地绑定为vue实例
-
注意事项:不应该使用箭头函数来定义计算属性函数例如 aDouble:()=>this.a*2。理由是箭头函数绑定了父级作用域的上下文。所以this将不会按照期望指向vue实例,this.a将是undefined。
-
案列
<!-- 计算属性模糊查询 -->
<div id="app">
<input type="text" v-model="mytext"/>
</div>
<ul>
<li v-for="data in datalistcom" :key="data">{{data}}</li>
</ul>
<script type="text/javascript">
var vm=new Vue({
el:'#app',
data:{
datalist:['aaa','bbb','ccc','ddd','aa','a','cc','dd'],
mytext:''
},
computed:{
datalistcom(){
return this.datalist.filter(item=>item.indexOf(this.mytext)>-1)
}
}
})
</script>
- 计算属性是为了模板中的表达式简洁,易维护,符合用于简单运算的初衷
- 两面性
- 注册在computed:{}中
- 两面性:注册的时候是方法,使用的时候写法类似于在调用数据
变量不在data中定义,而是定义在computed中,写法跟写方法一样,有返回值,函数名直接在页面模板中渲染,不加小括号 - 当作数据使用时,数据的值就是他的返回值
什么时候使用计算属性?
根据传入的变量的变化,进行结果的更新
- 缓存性
-
计算属性基于响应式依赖进行缓存,依赖的值未发生变化,它调用的就是上一次计算缓存中的数据,因此提高了程序的性能,而methods中每调用一次就会重新计算一次,为了进行不必要的资源消耗,就会选择用计算属性
-
第一次展示组件的时候马上执行一次方法,执行后结果存入缓存中。后续之后当依赖数据发生改变,才会再次执行方法。
扩展: 计算属性和监听属性的区别
- 计算属性变量在computed中定义,属性监听在data中定义
- 计算属性是声明式的描述一个依赖于其他值,依赖值改变后重新计算结果更新DOM,如果依赖的值不发生改变,直接使用缓存里的数据,并且初始化的时候就执行。属性监听的是定义的变量,当定义的值发生变化时,执行相对应的函数。
- 所以,computed适合在模块中渲染,某个值是依赖了其他的响应式对象甚至是计算属性计算而来,
而watch适合监听某个值的变化去完成一些复杂的业务逻辑。
watch:{} 监听器/侦听器/监听器/监听函数
- watch 一个对象,键是需要观察的表达式,值是对应回调函数,值也可以是方法名,或者包含选项的对象。vue实例将会在实例化时调用$watch,遍历watch中的每一个属性
var vm=new Vue({
data:{a:1,b:2,c:3},
watch:{
a:function(val,oldVal){
console.log('new:%s,old:%s',val,oldVal)
}
}
})
vm.a=2 //new:2 old:1
- 上面说到计算属性的时候初始化的时候就可以被监听到并且计算 但是watch是发生改变的时候才会触发
- 当你有一些数据需要随着其他数据变动而变动时,或者当需要在数据变化时执行异步或开销较大的操作时,你可以使用watch
- 案例
<body>
<div id="app">
<p>单价:<input type="text" v-model="price"/></p>
<p>数量:<input type="text" v-model="number"></p>
<p>计算金额:{{sum}}</p>
</div>
</body>
<script type="text/javascript">
var vm=new Vue({
el:'#app',
data:{
price:100,
number:1,
sum:0
},
//监听某一个值或者状态发生变化,变化就会触发watch
watch:{
//方法名和监听的参数名要一致
price(){
console.log(this.price)
if(this.price*this.number<1000&&this.price*this.number>0){
this.sum=this.price*this.number+100
}else{
this.sum=this.prthis.sum=this.price*this.numberice*this.number
}
},
number(){
console.log(this.price)
if(this.price*this.number<1000&&this.price*this.number>0){
this.sum=this.price*this.number+100
}
else{
}
}
}
})
</script>
- 使用方法
- 注册在watch属性中
- 以监听数据的名字来注册
- 当对应的数据发生改变的时候,会马上执行这个方法
- 两种方法: 函数形式和对象形式
1.函数形式
Watch:{
a(val1,val2){
// 方法中的固定参数
//参数1 数据改变后的值
//参数2 数据改变前的值
}
}//此例中,侦听数据a,a发生改变时,马上执行这个方法
2.对象形式
//注册成一个对象
watch(){
a:{
handler(val1,val2){
console.log('a发生了改变')
console.log(val1+'-----'+val2)
}
}
}
- watch对象形式里的方法和属性
-
方法:
handler 方法名是固定的,必须是handler,与简写时注册方法的效果相同
-
属性:
immediate 默认值是false 当值为true 第一次展示页面会马上执行一次侦听的方法。
deep(只有在对象形式中才能使用)深度监听,监听对象内部的变化,默认值为false,需要改成true。
- 常见的应用场景:配合防抖使用/类似于oninput事件效果
// 用户在网页一边输入内容,一边网页自己查找
watch:{
kws(){
this.search()
}
}
methods:{
search(){
clearTimeout(this.timer)
this.timer=setTimeout(()=>{
if(this.kws.trim()!==""){
console.log(`搜索和${this.kws}相关的内容...`)
}
},500)
}
}
props:{} 接收父组件的传参
- props可以是数组或对象,用于接收父组件的数据,props可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义校验和设置默认值
props:{
name:{
type:String
},
width:{
type:String
},
url:{
type:String
},
},
props:[name,width,url]
-
父子组件渲染时的通信
- 父组件获取的是同步数据
- 在父组件created阶段,父组件的初始化数据就已经传递给子组件的props
- 在父组件creatd阶段,把获取的同步数据赋值给初始化数据,不会触发update钩子函数,子组件加载也能拿到数据
- 父组件更新数据(触发update),比如父组件created钩子函数获取异步数据,子组件也会同步更新,但是先更新的是子组件里的数据
- 子组件更新props里的数据,父组件不但接收不到,而且还会报错。
- 父组件获取的是异步数据会有问题
-
产生问题的原因
父组件异步获取后台数据,这时候加载渲染数据生命周期已经走完,只能更新数据,触发更新渲染生命周期,所以子组件加载时,永远只能拿到父组件的初始数据,拿不到更新后的数据,但是,props是可以等的,页面最后是可以拿到异步的数据渲染的。 -
如何解决子组件加载获取不到父组件异步获取数据的问题。
- 方案1:使用v-if控制子组件渲染的时机,父组件拿到后台异步数据后,再渲染子组件加载的时候就能得到父组件的异步数据
- 方案2:子组件使用watch监听父组件传递过来的数据
这种方式父组件正常传递数据即可,不要做什么代码处理,只要再子组件加一个监听即可。
-
- 父组件获取的是同步数据
-
总结:【20230827】子组件props如果绑定动态数据,默认只在加载时传递,也就是说只传一次。props绑定视图层,可以传多次。父组件created赋值同步数据不会触发updated,同步数据可以在created时就传递给子组件。父组件赋值异步数据,触发update,子组件也会在update才能拿到数据,所以加载时只能拿到父组件的初始化数据。
filters:{} 自定义过滤器
- 对原始变量值加工再显示的这样一种特殊的函数。
- 有些数量值不能直接给人看时,需要用到过滤器。
- 过滤器的本质其实就是一个函数,这个函数只要把过滤器放在绑定语法里就会自动调用
- 如果数据使用过滤器,那么最终渲染到页面的是加工后的值
- 过滤器的创建
// 参数1:过滤器的名字
// 参数2:回调函数
1.全局创建(脚手架在main.js创建)
Vue.filter('moneyFilter',function(oldVal){
return `¥${oldval.toFixed(2)}`
})
2.在组件对象中创建
filters:{
uppercase:function(str){
return str.toUpperCase()
}
}
3.过滤器的使用
价格:{{price|moneyFilter}} //|是管道
- 过滤器的扩展(过滤器函数传实参的情况)
- vue中支持自定义过滤器,在使用过滤器时支持多重过滤并传参
- 过滤器函数中的第一个形参参数是固定的,待过滤的值,第二个才是过滤器函数自身传入的实参值。
<script>
Vue.filter('moneyFilter',function(oldVal,type){
return `${type||'¥'}${oldVal.toFixed(2)}`
})
</script>
<body>
<div id="app">
<h1>价格是:{{price1||moneyFilter('$')}}</h1>
<h1>价格是:{{price2||moneyFilter('¥')}}</h1>
</div>
<script>
new Vue({
el:"#app",
data:{
price1:12.5
price2:17.5
}
})
</script>
</body>
- 局部过滤器案例
<template>
<div class="demo05">
<h2>过滤器</h2>
<h3>{{sex|gender}}</h3>
<h3>{{salary|format(5)}}</h3>
</div>
</template>
<script>
export default{
data(){
return{
sex:1,
salary:10000
}
},
filters:{
gender:function(value){
return(value==1&&"男) || (value == 0 && "女")
},
format:function(value,n){
return value.toFixed(n)
}
}
}
</script>
- 封装过滤器(以封装全局过滤器为例)
Vue.filter('gender',function(value){
return(value==1&&"男) || (value == 0 && "女")
})
Vue.filter('format',function(value){
return value.toFixed(n)
})
1.封装方法1: 把以上代码封装到一个公共js文件中,再引入到全局文件main.js
const filters={
gender:function(value){
return(value==1&&"男) || (value == 0 && "女")
},
format:fucntion(value,n){
return value.toFixed(n)
}
}
export default filters
//main.js
import filters from './filter'
Object.keys(filters).forEach(val=>{
Vue.filters(val,filters[val])
})
2.封装方法2:直接在index.js中引入vue
directives:{} 自定义指令
-
除了默认设置的核心指定(v-model和v-show),Vue也允许注册自定义指令,注意,在vue2.0里面,代码复用的主要形式和抽象是组件—然而,有的情况下,你仍然需要对 纯DOM元素进行底层操作,这时候就需要用到自定义指令。
-
vue2.x 指令里提供的几个钩子函数
-
bind:指令被绑到元素上的时候使用,只会执行一次
-
inserted:绑定指令的元素被添加到父元素上的时候使用,只会执行一次
-
update:所在组建的VNode更新时调用,但是可能发生在其子VNode更新之前,也就是说元素更新, 但子元素尚未更新, 将调用此钩子( 自定义指令所在组件更新时执行, 但是不保证更新完成 ) —> 和自定义所在组件有关
-
componentUpdated:组件和子级更新后执行( 自定义指令所在组件更新完成, 且子组件也完成更新 ).
-
unbind:解绑(销毁) 自定义指令所在的dom销毁时执行,只调用一次.
-
-
钩子函数提供的参数
- 自定义指令中, 都不能直接使用this
- el:当前所在的dom元素
- binding 一个对象,表示当前指令上的属性、修饰符、value值等信息。 value常用
binding.arg: String,属性名。例如v-my-directive:foo中,属性名为"foo" binding.modifiers:Object, 包含所有修饰符的一个对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。 binding.name:String, 指令名 binding.rawNmae:String, 完整指令名,例如 v-my-directive:foo.bar=“1 + 1” 中, 完整指令名就是 v-my-directive:foo.bar=“1 + 1”。 binding.value:Any, 指令绑定的当前值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。 binding.expression:String, 解析的哪一个值、表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。 binding.oldValue:Any, 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。 binding.oldArg:Any, 指令属性名的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- vnode: 虚拟节点,可以获取当前指令所在的实例对象vnode.context
- oldvnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
-
案列1: 全局方法创建一个叫做v-focus的指令
//全局创建指令
//创建指令不加v-前缀
//大括号{}里写的是指令中提供的钩子函数,方法体写在钩子函数里
Vue.directive('focus',{
inserted(el){
el.focus()
}
})
//使用
//使用指令时,要加上v-
<input v-focus>
- 案列2:局部方法创建一个叫做v-fontSize的指令
//局部创建指令
directives:{
fontSize:{
bind :function(el,binding){// binding.value 绑定的当前值
el.style.fontSize = paseInt(binding.value) + 'px'
}
}
//使用
//使用指令时,要加上v-
<div v-fontSize="12"></div>
functional:true 当前组件为函数式组件
- functional:true 函数式组件
- 无状态
- 无实例
- 没有任何生命周期
- 轻量,渲染性强,适合只依赖于外部数据传递而变化的组件(展示组件,无逻辑和状态修改)
- 在template标签里标明functional
- 只接收props传值
- 不需要script标签
- 添加 functional: true 之后就是函数式组件functional
- 函数式组件没有创建组件实例,所有传统的通过this来调用的属性,在这里都需要通过context来调用
export default{
functional:true,
props:{
data:{
type:Object,
default:()=>{}
}
},
methods:{
getName(){
// 需要this来调用的属性 可需要context来调用
return context.props.data.name
}
}
}
delimiters 改变插值的赋值
-
默认的插值是双大括号{{}}。但有时我们会有需求更改这个插值的形式。
delimiters : [ '${' , '}' ]
-
现在我们的插值形式就变成了${}。代替了{{ }}
model
- 类型:{ prop?: string, event?: string }
- prop 也就是调用该组件的父组件中使用v-model指令绑定的属性
- event 对应的是修改prop指定属性的值的函数
// 子组件
<template>
<el-dialog
:visible="visible"
@close="onClose"
>
</el-dialog>
</template>
<script>
export default {
name: 'HDialog',
model: {
prop: 'visible',
event: 'closeModal'
},
props: {
visible: {
type: Boolean,
default() {
return false
}
}
},
methods: {
// 点击关闭抽屉层
onClose() {
this.$emit('close')
// 修改model中的值
this.$emit('closeModal', false)
}
}
}
</script>
// 父组件
<h-dialog v-model="visible"></h-dialog>
inheritAttrs vue2.4中新增
- 默认
inheritAttrs: true
- 阻止父组件中传递的参数,子组件中不接受,参数仍然作用在子组件的根元素上
- 子元素中设置:
inheritAttrs: true
extends 扩展
- 类似于mixin,相当于继承,但是只是继承options Api中的内容,不继承template模板。
mixins:[] 混入器 注册混入对象
-
一个包含组件选项对象的数组,这些选项都被混入到当前组件的实例中
-
类型
interface ComponentOptions{ mixins?:ComponentOptions[] }
-
mixin选项接受一个mixin对象数组。这些mixin对象可以像普通的实例对象一样包含实例选项,它们将使用一定的选项合并逻辑与最终的选项进行合并。举例来说,如果你的 mixin 包含了一个 created 钩子,而组件自身也有一个,那么这两个函数都会被调用。Mixin 钩子的调用顺序与提供它们的选项顺序相同,且会在组件自身的钩子前被调用。
const mixin = { created() { console.log(1) } } export default{ created() { console.log(2) }, mixins: [mixin] }) // => 1 // => 2
-
一些方法一类的东西
-
把这些方法封装在一个对象中(一个js文件)
-
用混入的方式,把这个对象混入到每个组件中
-
那么,这个对象中的属性就可以混入到组件的vue对象中
-
既然,要混入到vue对象中,所以这个对象中的属性只能是vue实例中的属性
-
注意(mixin不再推荐):在 Vue 2 中,mixins 是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了 mixins 支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式。
1.新建混入器对象
export default{
data(){
return{
a:5,
b:7
}
},
methods:{
fn(){
alert(123)
}
}
}
2.将混入器对象文件引入到组件中
import mix from '../../mixin.js'
3.在vue对象的mixins属性中注册混入对象
export default{
name:'mixin',
//注册混入对象
mixin:[mix]
}
4.在视图层使用注册的混入对象
<template>
<div>
<span>{{a}}{{b}}</span>
<button @click="fn">弹出</button>
</div>
</template>
-
注意
当前组件和混入对象产生冲突时,冲突的内容如果非此即彼,那么就使用当前组件的
–
propsData 单页面应用中保持状态和数据
-
propsData 不是和属性有关,它用在全局扩展时进行传递数据。 类型:{ [key: string]: any }。 限制:只用于 new 创建的实例中。 详细:创建实例时传递 props。主要作用是方便测试。首先这个使用率并不高,因为后面会有vuex,他的作用就是单页面应用中保持状态和数据的,本机就是在全局扩展里面传递参数propsData。
-
举例
<div id="app"> </div> <template id="eg"> <div> <h5>这个是使用extend创建出来的组件</h5> </div> </template> <script src="./Vuejs/vue2-6-12.js"></script> <script> // 通过extend扩展一个新的组件 var myextend = Vue.extend({ template:"#eg" }) // 实例化对象 new myextend().$mount("#app"); </script>
-
我们可以看到这是使用extend方法创建组件,但是在使用这个方法的时候,我们是不容易传递数据的,因此这里引入了propsData。
<div id="app"> </div> <template id="eg"> <div> <h5>这个是使用extend创建出来的组件</h5> <h6>{{news}}</h6> </div> </template> <script src="./Vuejs/vue2-6-12.js"></script> <script> // 通过extend扩展一个新的组件 var myextend = Vue.extend({ template:"#eg", props:['news']//需要通过props来接收,接收的名称得和propsData的key相同 }) // 实例化对象 new myextend({propsData:{news:"这个就是propsData传递的数据"}}).$mount("#app"); </script>
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!