当前位置: 首页 > news >正文

前端框架Vue3(二)——Vue3核心语法之OptionsAPI与CompositionAPI与setup

Vue3核心语法

OptionsAPI与CompositionAPI

  • Vue2API设计是Options(配置)风格的。
  • Vue3API设计是Composition(组合)风格的。

Options APl的弊端

Options类型的API,数据、方法、计算属性等,是分散在:datamethodscomputed中的,若想新
增或者修改一个需求,就需要分别修改:datamethodscomputed,不便于维护和复用。

Composition API的优势

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

拉开序幕的setup

setup概述

  1. setupVue3中一个新的配置项,值是一个函数,它是Composition API“表演的舞台”,组件中所用到的:数
    据、方法、计算属性、监视等等,均配置在setup中。
    特点如下:
  • setup函数返回的对像中的内容,可直接在模板中使用。
  • setup中访问thisundefined
  • setup函数会在beforeCreate之前调用,它是"领先"所有钩子执行的。
  1. setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
    3. 注意点:
      1. 尽量不要与ue2.x配置混用
        • Vue2.x配置(datamethodscomputed)中可以访问setup中的属性、方法
        • 但在setup中不能访问到Vue2.x配置(datamethodscomputed)。
        • 如果有重名,setup优先。
      2. setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。

ref创建:基本类型的响应式数据

  • 作用:定义响应式变量。
  • 语法:let xxx=ref(初始值)
  • 返回值:一个RefImpl的实例对象,简称ref对象ref,ref对象的value属性是响应式的,
  • 注意点:
    • JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。
    • 对于let name=ref('张三')来说,name不是响应式的,name.value是响应式的。
<template><div class="person"><h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="showSex">查看性别</button></div>
</template><script lang="ts">export default {name:'PersonComponent',}
</script>
<script lang="ts" setup name='PersonComponent'>
import { ref } from 'vue';const name = ref("张三")const age = ref(18)const sex = "男"// 方法function changeName() {name.value = "王五"console.log(name)}function changeAge() {age.value+=1}function showSex() {alert(sex)}
</script>
<style  scoped>
.person{background-color: pink;box-shadow: 0 0 10px ;border-radius: 10px;padding: 20px;
}
button{margin-right: 10px;
}
</style>

reactive创建:对象类型的响应数据

<template><div class="person"><h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2><button @click="changePrice">修改汽车的价格</button><br><h2>游戏列表:<ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul></h2><button @click="changeFirstGameName">修改游戏</button></div>
</template><script lang="ts" setup name="Person">
import { defineComponent, reactive } from 'vue';const car = reactive({brand: 'Benz',price: 100
});const games = reactive([{ id: "1", name: "王者荣耀" },{ id: "2", name: "英雄联盟" },{ id: "3", name: "和平精英" }
])console.log(car)function changePrice() {car.price += 10;
}function changeFirstGameName() {games[0].name = "羊了个羊"
}
defineComponent({name: 'PersonInfo'
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

ref创建:对象类型的响应数据

<template><div class="person"><h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2><button @click="changePrice">修改汽车的价格</button><br><h2>游戏列表:<ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul></h2><button @click="changeFirstGameName">修改游戏</button></div>
</template><script lang="ts" setup name="Person">
import { defineComponent, ref } from 'vue';const car = ref({brand: 'Benz',price: 100
});const games = ref([{ id: "1", name: "王者荣耀" },{ id: "2", name: "英雄联盟" },{ id: "3", name: "和平精英" }
])console.log(car)function changePrice() {car.value.price += 10;}function changeFirstGameName() {games.value[0].name = "羊了个羊"}
defineComponent({name: 'PersonInfo'
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

ref对比reactive

  • 宏观角度看:
    1. ref用来定义:基本类型数据对象类型数据
    2. reactive用来定义:对象类型数据
  • 区别:
    1. ref创建的变量必须使用.value(可以使用volar插件自动添加.value)。
    2. reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。
  • 使用原则:
    1. 若需要一个基本类型的响应式数据,必须使用ref
    2. 若需要一个响应式对象,层级不深,refreactive都可以。
    3. 若需要一个响应式对象,且层级较深,推荐使用reactive

<template><div class="person"><h2>一辆{{ car.brand }}车,价值{{ car.price }}</h2><button @click="changePrice">修改汽车的价格</button><button @click="changeBrand">修改汽车的品牌</button><button @click="changeCar">修改汽车</button><hr><h2>当前求和为:{{ sum }}</h2><button @click="changeSum">sum+1</button></div>
</template><script lang="ts" setup name="Person">
import { defineComponent, ref } from 'vue';// const car = reactive({
//   brand: 'Benz',
//   price: 100
// });
const car = ref({brand: 'Benz',price: 100
});
const sum = ref(0);// function changePrice() {
//    car.price += 10;
//  }
// function changeBrand() {
//    car.brand = 'hongqi';
//  }
function changePrice() {car.value.price += 10;}
function changeBrand() {car.value.brand = 'hongqi';}function changeCar() {// Object.assign(car, {//   brand: 'Aodi',//   price: 10// });car.value = {brand: 'Aodi',price: 10};}
function changeSum() {sum.value += 1;}
defineComponent({name: 'PersonInfo'
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

toRefs与toRef

  • 作用:将一个响应式对象中的每一个属性,转换为ref对象。
  • 备注:toRefstoRef功能一致,但toRefs可以批量转换
  • 语法如下:
<template><div class="person"><h2>name:{{ name }}</h2><h2>age:{{ age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button></div>
</template><script lang="ts" setup name="Person">import { defineComponent, reactive, toRefs } from 'vue';const person = reactive({name: '张三',age: 18,})defineComponent({name: 'PersonComponent',})const {name,age} = toRefs(person);function changeName() {name.value += '三';}function changeAge() {age.value += 1;}</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

计算属性(computed)

<template><div class="person">姓:<input type="text" v-model="firstname"><br>名:<input type="text" v-model="lastname"><br><button @click="changeFullName()">将全名改为li-si</button>全名:<span>{{ fullname}}</span></div>
</template><script lang="ts" setup>
import { computed, ref } from 'vue';const firstname = ref('张');
const lastname = ref('三');
// 这样定义的fullname是一个计算属性,且是只读的
// const fullname=computed(() => {
//   return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1)+'-'+ lastname.value;
// });
const fullname = computed( {get(){return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1)+'-'+ lastname.value;},set(val){const [str1,str2]=val.split('-');firstname.value=str1lastname.value=str2}
});function changeFullName() {fullname.value='李-四'
}
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

watch

  • 作用:监视数据的变化(和Vue2中的watch作用一致)
  • 特点:Vue3中的watch只能监视以下四种数据
    1. ref定义的数据。
    2. reactive定义的数据。
    3. 函数返回一个值(getter函数)。
    4. 一个包含上述内容的数组。
      我们在Vue3中使用watch的时候,通常会遇到以下几种情况:
*情况一

监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变。

<template><div class="person"><h1>情况一:监视【ref】定义的【基本类型】数据</h1><h2>当前求和为:{{ sum }}</h2><button @click="changeSum">点击sum+1</button></div>
</template><script lang="ts" setup>
import { ref, watch } from 'vue';const sum=ref(0)function changeSum() {sum.value+=1;
}
// 监视
watch(sum,(newValue,oldValue)=>{console.log('sum被修改了',newValue,oldValue)
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>
*情况二

监视ref定义的【对像类型】数据:直接写数据名,监视的是对像的【地址值】,若想监视对像内部的数据,要
手动开启深度监视。
注意:

  • 若修改的是ref定义的对象中的属性,newValueoldValue都是新值,因为它们是同一个对象。
  • 若修改整个ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。
<template><div class="person"><h1>情况二:监视【ref】定义的【对象类型】数据</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改人</button></div>
</template><script lang="ts" setup name="Person">
import { ref, watch } from 'vue';const person = ref({name:'张三',age:18
})function changeName(){person.value.name += '五'
}function changeAge(){person.value.age += 1
}function changePerson(){person.value = {name:'李四',age:90}
}
// 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内容属性的变化,需要手动开启深度监视
// watch(person,(newValue,oldValue)=>{
//   console.log('person变化了',newValue,oldValue)
// })
// 监视`ref`定义的【对像类型】数据:直接写数据名,监视的是对像的【地址值】,若想监视对像内部的数据,要手动开启深度监视
// watch的第一个参数是监视的数据
// watch的第二个参数是监视的回调
// watch的第二个参数是配置对象()
watch(person,(newValue,oldValue)=>{console.log('person变化了',newValue,oldValue)
},{deep:true,immediate:true})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>
*情况三

监视reactive定义的【对象类型】数据,且默认开启了深度监视。

<template><div class="person"><h1>情况三:监视【ref】定义的【对象类型】数据</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改人</button></div>
</template><script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';const person = reactive({name:'张三',age:18
})function changeName(){person.name += '五'
}function changeAge(){person.age += 1
}function changePerson(){Object.assign(person,{name:'王五',age:19})
}
//监视,情况三:监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。
watch(person,(newValue,oldValue)=>{console.log('person被修改了',newValue,oldValue)
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>
*情况四

监视refreactive定义的【对象类型】数据中的某个属性,注意点如下:

  1. 若该属性值不是【对象类型】,需要写成函数形式。
  2. 若该属性值是依然是【对象类型】,可直接编,也可写成函数,不过建议写成函数。
    结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,
    需要手动开启深度监视。
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changeFirstCar">修改第一台车</button><button @click="changeSecondCar">修改第二台车</button><button @click="changeCar">修改车</button></div>
</template><script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';
const person = reactive({name: '张三',age: 18,car:{c1:"Benz",c2:"BMW"}
});function changeName() {person.name += '三';}function changeAge() {person.age += 1;}function changeFirstCar() {person.car.c1 ='hongqi'}function changeSecondCar() {person.car.c2 ='Aodi'}function changeCar() {person.car.c1 ='hongqi';person.car.c2 ='Aodi';}watch(()=>{return person.name},(newValue, oldValue) => {console.log('person.name被修改了', newValue, oldValue);})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>
*情况五

情况五:监视多个数据

<template><div class="person"><h1>情况五:监视多个数据</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changeFirstCar">修改第一台车</button><button @click="changeSecondCar">修改第二台车</button><button @click="changeCar">修改车</button></div>
</template><script lang="ts" setup name="Person">
import { reactive, watch } from 'vue';
const person = reactive({name: '张三',age: 18,car:{c1:"Benz",c2:"BMW"}
});function changeName() {person.name += '三';}function changeAge() {person.age += 1;}function changeFirstCar() {person.car.c1 ='hongqi'}function changeSecondCar() {person.car.c2 ='Aodi'}function changeCar() {person.car.c1 ='hongqi';person.car.c2 ='Aodi';}// 监视,情况五:监视上述多个数据watch([()=>person.name,()=>person.car.c1] ,(newValue, oldValue) => {console.log("person.car被修改了", newValue, oldValue);},{deep:true})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

watchEffect

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
  • watch对比watchEffect
    1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
    2. watch:要明确指出监视的数据
    3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
      示例代码:
<template><div class="person"><h2>需求:当水温达到60度,或水位达到80cm,给服务器发送请求</h2><h2>当前水温为:{{ temp }}°C</h2><h2>当前水位为:{{ height }}cm</h2><button @click="changeTemp">点我temp+10</button><button @click="changeHeight">点我height+10</button></div>
</template><script lang="ts" setup name="Person">
import { ref, watchEffect } from 'vue';
const temp = ref(10);
const height = ref(0);
function changeTemp() {temp.value+=10
}
function changeHeight() {height.value+=10
}
// 监视 watch实现
/*watch([temp,height],(value)=>{
const [newTemp,newHeight]=value
console.log(newTemp,newHeight)
if(newTemp>=60||newHeight>=80){
console.log("预警!")
}
})*/// 监视 watchEffect实现
watchEffect(()=>{if(temp.value>=60||height.value>=80){console.log("预警!")}
})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

【标签的ref属性】

作用:用于注册模板引用。

  • 用在普通DOM标签上,获取的是DOM节点。
  • 用在组件标签上,获取的是组件实例对象。
    用在普通DOM标签上:
<template><div class="person"><h1>中国</h1><h2 ref="title2">河南</h2><h3>前端</h3><button @click="showLog">点我输出h2</button></div>
</template><script lang="ts" setup name="Person">
import { defineExpose, ref } from 'vue';
//创建一个title2,用于存储ref标记的内容
const title2 = ref()
const a = ref(1)
const b = ref(2)
const c = ref(3)
function showLog() {console.log(title2.value)
}
defineExpose({a,b,c})
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

回顾TS中的接口泛型自定义类型

<template><div class="person">
<h3>{{ person }}</h3>
<h3>{{ personList }}</h3></div>
</template><script lang="ts" setup name="Person">
import { type PersonInter, type Persons } from '@/types';const person:PersonInter={id:'1',name:'张三',age:18}
const personList:Persons=[{id:'1',name:'张三',age:18},{id:'2',name:'李四',age:20},{id:'3',name:'王五',age:30}
]
</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

props的使用

//Person.vue
<template><div class="person"><ul><li v-for="item in list" :key="item.id">{{ item.name }}--{{ item.age }}</li></ul></div>
</template><script lang="ts" setup name="Person">
import { withDefaults } from 'vue';
import {type Persons } from '../types/index';// 只接收list
// defineProps(['list'])// 接收list+限制类型
// defineProps<{list:Persons}>()//接收list+限制类型+限制必要性+指定默认值
// withDefaults(defineProps<{list?:Persons}>(),{
//   list:()=>[{id:'1',name:'haha',age:18}]
// })// 接收a,同时将props保存起来
// const x=defineProps(['a'])
// console.log(x)</script><style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>
//App.vue
<template><Person a="haha" :list="personList"/>
</template><script lang="ts" setup name="App">
import Person from './components/Person.vue';
import {reactive} from 'vue';
import {type Persons} from "@/types";
const personList=reactive<Persons>([{id:'1',name:'李四',age:18,x:10},{id:'2',name:'王五',age:20,},{id:'3',name:'张三',age:35,}
])
</script>

生命周期

人的生命周期:

【时刻】【要做的事】
出生
经历哭、笑
死亡遗嘱

组件的生命周期:

【时刻】【调用特定的函数】
创建created
挂载mounted
更新
销毁
  • 概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机,调用特定的函
    数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子
  • 规律:
    生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。
  • Vue2的生命周期
    • 创建阶段:beforeCreatecreated
    • 挂载阶段:beforeMountmounted
    • 更新阶段:beforeUpdateupdated
    • 销毁阶段:beforeDestroydestroyed
  • Vue3的生命周期
    • 创建阶段:setup
    • 挂载阶段:onBeforeMountonMounted
    • 更新阶段:onBeforeUpdateonUpdated
    • 销毁阶段:onBeforeUnmountonUnmounted
  • 常用的钩子:onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)
  • 示例代码:
<template><div class="person"><h2>当前求和为:{{ sum }}</h2><button @click="add">点我sum+1</button></div>
</template><script lang="ts" setup name="Person">
import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from 'vue'
// 数据
const sum=ref(0)// 方法
function add() {sum.value+=1
}// 创建
console.log("子--创建成功")
// 挂载
onBeforeMount(()=>{console.log("子--挂载成功")
})
// 挂载完毕
onMounted(()=>{console.log("子--挂载完毕")
})
// 更新前
onBeforeUpdate(()=>{console.log("子--更新前")
})
// 更新后
onUpdated(()=>{console.log("子--更新后")
})
// 销毁前
onBeforeUnmount(()=>{console.log("子--销毁前")
})
// 销毁后
onUnmounted(()=>{console.log("子--销毁后")
})
</script>
<style scoped>
.person {background-color: pink;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;
}
button {margin-right: 10px;
}
</style>

自定义hooks

import { computed, onMounted, ref } from 'vue'
export default function () {// 数据const sum = ref(0)const bigSum=computed(()=>{return sum.value*10})// 方法function add() {sum.value += 1}// 钩子onMounted(() => {add()})
// 向外部提供东西return {sum,add,bigSum}
}
import axios from 'axios'
import { reactive,onMounted } from 'vue'export default function (){// 数据
const dogList = reactive(['https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
])
// 方法
async function addDog() {try {const result = await axios.get('https://dog.ceo/api/breeds/image/random')if (result.data && result.data.message) {dogList.push(result.data.message)}} catch (error) {console.error('获取狗狗图片失败:', error)}
}
// 钩子
onMounted(() => {addDog()
})
//向外部提供东西
return {dogList, addDog}
}
import { computed, onMounted, ref } from 'vue'
export default function () {// 数据const sum = ref(0)const bigSum=computed(()=>{return sum.value*10})// 方法function add() {sum.value += 1}// 钩子onMounted(() => {add()})
// 向外部提供东西return {sum,add,bigSum}
}
http://www.lryc.cn/news/605470.html

相关文章:

  • AWS云安全审计终极实战指南:构建自动化安全防线
  • 数字化应急预案:构筑现代安全防线
  • Web3:在 VSCode 中使用 Vue 前端与已部署的 Solidity 智能合约进行交互
  • 从渠道渗透到圈层渗透:开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新路径研究
  • 【09】大恒相机SDK C#开发 ——库函数 IntPtr ConvertToRGB24详细解释 及 示例
  • 【JEECG】JVxeTable表格拖拽排序功能
  • 动态规划Day5学习心得
  • python的异步、并发开发
  • (C++)C++类和类的方法(基础教程)(与Python类的区别)
  • C++提高编程学习--模板
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-50,(知识点:TCP/IP 模型)
  • 磁盘IO优先级控制对美国服务器存储子系统的调优验证
  • 02 基于sklearn的机械学习-KNN算法、模型选择与调优(交叉验证、朴素贝叶斯算法、拉普拉斯平滑)、决策树(信息增益、基尼指数)、随机森林
  • 【动态规划 | 多状态问题】动态规划求解多状态问题
  • YOLO---01目标检测基础
  • windows环境下MySQL 8.0 修改或重置密码
  • Mybatis分页查询当前页数据条数大于实际返回的数据条数
  • 7.5将模块分离到不同的文件中
  • 【JEECG】BasicTable单元格编辑,插槽添加下拉组件样式错位
  • 跳跃表可视化深度解析:动态演示数据结构核心原理
  • Docker-01.Docker课程介绍
  • 分层解耦(Controller,Service,Dao)
  • 从映射到共生:元宇宙、物联网与AI的智能融合生态图谱
  • nav2--安装/教程
  • 如何保证数据库的持久性与一致性:从 Linux 磁盘缓存策略到 MySQL 的设计
  • [SKE]使用gmssl库实现AES、SM4、DES、RSA、3DES_EDE和3DES_EEE算法的加密/解密参考模型
  • GitPython01-依赖排查
  • 8. 网络层
  • Linux系统编程Day1-- 免费云服务器获取以及登录操作
  • 【25届数字IC秋招总结】面试经验12——海康威视