1.setup1.setup:Vue3的新配置项,值为一个函数.2.setup时所有Composition API(组合APi)'表演舞台'3.组件中所用到的:数据、方法等等,均要配置在setup中4.setup函数的两种返回值:若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用若返回一个渲染函数,则可以自定义渲染内容.5.注意点:1.尽量不要与Vue2配置混用Vue2配置(data、methods、computed...)中可以访问setup中的属性、方法但在setup中不能访问到Vue2配置(data、methods、computed...)如果有重名,setup优先2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性.6.setup的语法糖 在 标签里面 加上一个setup 可以不用return直接在script中写我们的代码 2.ref函数作用:定义一个响应式数据语法:const xxx=ref(initValue)创建一个包含响应式数据的引用对象(reference对象,简称ref对象)JS中操作数据:xxx.value模板中读取数据,不需要.value,直接 {{xxx}} 备注接收的数据可以是:基本类型,也可以是对象类型基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的对象类型的数据:内部求助了Vue3的一个新函数---reactive函数3.reactive函数作用:定义一个对象类型的响应式数据(基本类型别用他,用r ef函数)语法:const 代理对象=reactive(被代理对象)接收一个对象(或数组),返回一个代理器对象(proxy对象)reactive定义的响应式数据时深层次的内部基于ES6的Proxy实现,通过代理对象操作对象内部数据都是响应式的4.Vue3中的响应式原理Vue2.x的响应式实现原理:对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截、数据劫持数组类型:通过重写更新数组的一系列方法实现拦截.(对数据的变更方法进行了包裹).Object.defineProperty(data,'count',{ get(){}, set(){}, })存在问题:新增属性、删除属性,界面不会更新.直接通过下标修改数组,界面不会自动更新.(.$set .$delete去处理)Vue3的响应式实现原理:通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加.通过Reflect(反射):对被代理的属性进行操作.MDN文档中描述的Proxy与Reflect:Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ProxyReflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect new Proxy(data, { // 拦截读取属性值 get(target, prop) {https://www.baiyeovo.cn/admin/write-post.php?cid=290#wmd-editarea return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set(target, prop, value) { return Reflect(target, prop, value) }, //拦截删除属性 deleteProperty(target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom' 4.reactive对比ref从定义数据角度对比:ref用来定义:基本数据类型reactive用来定义:对象(或数组)类型数据备注:ref也可用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象.从原理角度对比ref通过Object.defineProperty()的get与set来实现响应式(数据劫持).reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据.从实用角度来对比ref定义数据:操作数据需要 .value,读取数据时模板中直接读取不需要.valuereactive定义的数据:操作数据与读取数据,均不需要.value5.setup的两个注意点setup执行的时机在beforeCreated之前执行一次,this是undefined.setup的参数props:值为对象,包含:组件外部传递过来,且组件内部 声明接收了的属性context:上下文对象attrs:值为对象,包含:组件外部传递过来,但是没有在props配置中声明的属性,相当于this.$attrs.slot:收到的插槽内容,相当与this.$slot.emit:分发自定义事件的函数,相当于this.$emit.6.计算属性与监视与Vue2.x中的computed配置功能一致写法import {computed} from 'vue' setup() { //数据 let person = reactive({ firstName: '张', lastName: '三' }) //计算属性--简写(没有考虑计算属性被修改的情况 ) // person.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] } }) return { person } }与Vue2.x中watch配置功能一致两个小坑:监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视（deep配置无效）监视reactive定义的响应式数据中的某个属性时:deep配置有效总结:监测基本数据类型(有oldValue) 监测reactive定义的数据(没有oldValue) //情况一:监测ref所定义的一个响应式数据 watch(sum,(newVal,oldVal)=>{ console.log('sum变了',newVal,oldVal) },{immediate:true}) //情况二:监视ref所定义的多个响应式数据 watch([sum,msg],(newVal,oldVal)=>{ console.log('person变了',newVal,oldVal) },{immediate:true}) // 情况三:监视reactive所定义的一个响应式数据, // 1.注意:此处无法正确获取oldValue // 2.注意:强行开启了深度监视(deep配置无效) watch(person,(newVal,oldVal)=>{ console.log('person变了',newVal,oldVal) },{deep:false}) //情况四:监视react所定义的一个响应式数据中的某个属性 watch(()=>person.age,(newVal,oldVal)=>{ console.log('person的age变了',newVal,oldVal) }) // 情况五:监视reactive所定义的一个响应式数据中的某些属性 watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{ console.log('person的jname或age变化了',newVal,oldVal) }) //特殊情况 watch(()=>person.job,(newVal,oldVal)=>{ console.log('person的job.变化了',newVal,oldVal) },{deep:true}) //此处由于监视的时reactive所定义的对象中的某个属性,所以deep配置有效 v-model语法糖在vue2.0中v-mode语法糖简写的代码 <Son :value="msg" @input="msg=$event" />在vue3.0中v-model语法糖有所调整：<Son :modelValue="msg" @update:modelValue="msg=$event" />总结： vue3.0封装组件支持v-model的时候，父传子:modelValue 子传父 @update:modelValue补充： vue2.0的 xxx.sync 语法糖解析 父传子 :xxx 子传父 @update:xxx 在vue3.0 使用 v-model:xxx 代替。 子组件 {{modelValue}} 改变数据 watchEffect 函数watch的套路是:既要指明监听的属性,又要指明监听的函数watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性watchEffect有点像computed:但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值watchEffect(()=>{ const x1=sum.value const x2=person.age console.log('watchEffect配置的回调执行了'); })Vue3生命周期函数Vue3.0 中可以继续使用Vue2.x中的生命周期钩子,但有两个被更名:beforeDestroy改名为 beforeUnmountdestoryed改名为UnmountedVue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x钩子对应关系如下:beforecreated===>setup()created===>setup()beforeMount===>onBeforeMountmounted===>onMonuntbeforeUpdate===>onBeforeUpdateUpdated===>onUpdatebeforeDestroy===>onBeforeUnmountdestoryed===>onUnmounted自定义hook函数什么是hook ---本质是一个函数,把setup中使用的composition API 进行了封装类似于Vue2.x中的mixin自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂toRef作用:toRef是函数，转换响应式对象中某个属性为单独响应式数据，并且值是关联的。语法:`const name=toRef(person,'name')应用:要将响应式对象中的某个属性单独提供给外部使用扩展:toRefs与toRef功能一致,但可以创建多个ref对象,语法:toRefs(person)toRefstoRefs是函数，转换响应式对象中所有属性为单独响应式数据，对象成为普通对象，并且值是关联的其他Composition API1.shallowReactive与shallowRefshallowReactive:只处理对象最外层的响应式(浅响应式)shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理什么时候使用?如果有一个对象数据,解构比较深,但变化时只是最外层属性变化===>shallowReactive如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换===>shallowRef2.readonly与shallowReadonlyreadonly:让一个响应式数据变为只读(深只读)shallowReadonly:让一个响应式数据变为只读(浅只读)应用场景:不希望数据被修改时3.toRaw与markRawtoRaw:作用:将一个由reactive生成的响应式对象转为普通对象使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新markRaw作用:标记一个对象,使其永远不会再成为响应式对象应用场景: 1.有些值不应该被设置为响应式的,例如复杂的第三方类库等. 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能.4.customRef作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制实现防抖效果: {{ keyWord }} 5.provide与inject作用:实现祖孙组件间通信套路:父组件有一个provide,子组件有一个inject选项来开始使用这些数据具体写法: 1.祖组件中:setup(){ ..... let car=reactive({name:'奔驰',price:'40万'}) provide('car',car) ..... }2.孙组件中:setup(props,context){ ..... const car=inject('car') return{ car} .......6.响应式数据的判断isRef:检擦一个值是否为一个ref对象isReactive:检查一个对象是否由reactive创建的响应式代理isReadonly:检查一个对象是否由readonly创建的只读代理isProxy:检查一个对象是否由reactive或者readonly方法创建的代理新的组件1.Fragment在vue2中:组件必须有一个根标签在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中好处:减少标签层级,减少内存占用2.Teleport什么时Teleport? ----Teleport是一种能够将我们组件html结构移动到指定位置的技术<teleport top="移动位置"> 我是一个弹窗 关闭弹窗 <teleport>3.Suspense等待异步组件渲染时一些后备内容,获得更好的用户体验异步引入组件import {defineAsyncComponent} from 'vue' const Child =defineAsyncComponent(()=>import(./component/Child.vue))使用Suspense包裹组件,并配置好default与fallback 我是App组件 <Suspense> <Child></Child> 加载中.... </Suspense> Vue3自定义指令的生命周期app.directive('directiveName', { // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时，这很有用 created() {}, // 当指令第一次绑定到元素并且在挂载父组件之前调用 beforeMount() {}, // 在绑定元素的父组件被挂载后调用 mounted() {}, // 在更新包含组件的 VNode 之前调用 beforeUpdate() {}, // 在包含组件的 VNode 及其子组件的 VNode 更新后调用 updated() {}, // 在卸载绑定元素的父组件之前调用 beforeUnmount() {}, // 当指令与元素解除绑定且父组件已卸载时, 只调用一次 unmounted() {}, });vue3获取dom元素获取单个元素 获取单个DOM元素 获取多个dom元素(一般用于获取数组) 获取多个DOM元素 {{ item }}
把date的数据处理成data2这样的最简单的一种方法:Object.valuesconst obj = {}; data.forEach((item)=> { const { a , b , c} = item if(!obj[a]){ obj[a] = {a,list:[{b , c}]} }else{ obj[a]['list'].push({b , c}) } }) console.log(Object.values(obj),'obj')element中Pagination分页功能的自定义序号//自定义序号 <el-table-column type="index" :index="indexMethod" label="序号" width="120" /> //配置自定义函数 indexMethod(index) { return (this.page - 1) * this.pagesize + index + 1 } // 解决删除时只有一条数据 if (this.list.length === 1 && this.page > 1) { this.page-- } //然后重新渲染数据效果:数组扁平化递归 const newArr = arr => [].concat(...arr.map(item => Array.isArray(item) ? newArr(item) : item)) console.log(newArr([1, [2, 3], [4, [5], 6], 7]));flat方法--二维数组扁平化flat 用于将多维数组拉平（扁平化），不影响原数组，返回新的数组。[1, 2, [3, [4]]].flat() // [1, 2, 3, [4]] 仅有一个参数depth，用于指定拉平的深度，默认值为1。若depth指定为非正数，将返回原数组，指定为Infinity，无论多少层都将扁平化为一维数组。例如:[1, 2, [3, [4]]].flat(2) // [1, 2, 3, 4] [1, 2, [3, [4]]].flat(0) // [1, 2, [3, [4]]] [1, 2, [3, [4]]].flat(Infinity) // [1, 2, 3, 4]Array.fromArray.from() 是用来把伪数组转真数组的console.log(Array.from('foo')); // expected output: Array ["f", "o", "o"] console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6] 生成一个数字6-72的数组 Array.from({ length: 67 }).map((item,index)=>index+6)Array.from()可以接收两个参数,第一个参数时伪数组,第二个是一个回调函数(item,index)=>{}可以用来操作第一个参数 如上所示new Array()new Array(1,2) 参数长度大于一,则生成一个由参数组成的新数组 //[1,2] new Array(10) 参数只有一个时,生成数组,长度为传入的参数.值为空 //[空属性 × 10]字符串去0String.prototype.startsWith()startsWith() 方法用来判断当前字符串是否以另外一个给定的子字符串开头，并根据判断结果返回 true 或 falseconst str1 = 'Saturday night plans'; console.log(str1.startsWith('Sat')); // expected output: true console.log(str1.startsWith('Sat', 3)); // expected output: false 参数传0,根据返回值处理即可补零String.prototype.padStart()padStart() 方法用另一个字符串填充当前字符串 (如果需要的话，会重复多次)，以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充。'abc'.padStart(10); // " abc" 'abc'.padStart(10, "foo"); // "foofoofabc" 'abc'.padStart(6,"123465"); // "123abc" 'abc'.padStart(8, "0"); // "00000abc" 'abc'.padStart(1); // "abc"小程序获取数据返回的带有div的字符串小程序获取数据返回的带有div的字符串,渲染出来后image有空白间隙问题 // 使用字符串的 replace() 方法，为 img 标签添加行内的 style 样式，从而解决图片底部空白间隙的问题 res.message.goods_introduce = res.message.goods_introduce.replace(/<img /g, '<img style="display:block;" ')
当我们使用element组件库的Tree组件时，我们需要一个树形数据才能进行渲染示例数据:const arr = [ { id: '01', name: '张大大 项目经理', pid: '', job: '项目经理' }, { id: '02', name: '小亮 产品leader', pid: '01', job: '产品leader' }, { id: '03', name: '小美', pid: '01', job: 'UIleader' }, { id: '04', name: '老马 技术leader', pid: '01', job: '技术leader' }, { id: '05', name: '老王 测试leader', pid: '01', job: '测试leader' }, { id: '06', name: '老李 运维leader', pid: '01', job: '运维leader' }, { id: '07', name: '小丽 产品经理', pid: '02', job: '产品经理' }, { id: '08', name: '大光 产品经理', pid: '02', job: '产品经理' }, { id: '09', name: '小高 UI设计师', pid: '03', job: 'UI设计师' }, { id: '10', name: '小刘 前端工程师', pid: '04', job: '前端工程师' }, { id: '11', name: '小华 后端工程师', pid: '04', job: '后端工程师' }, { id: '12', name: '小李 后端工程师', pid: '04', job: '后端工程师' }, { id: '13', name: '小赵 测试工程师', pid: '05', job: '测试工程师' }, { id: '14', name: '小强 测试工程师', pid: '05', job: '测试工程师' }, { id: '15', name: '小涛 运维工程师', pid: '06', job: '运维工程师' }, { id: '16', name: '小才 运维工程师', pid: '07', job: '运维工程师' }, ]方法:递归/** * 将列表型的数据转换成树形数据 => 递归算法 * 这里是要建立树形结构, 所以一定要找一个头 */ export function tranListToTreeData(list, rootValue) { const arr = [] list.forEach(item => { if (item.pid === rootValue) { // 以 item.id 作为 父 id, 接着往下找 const children = tranListToTreeData(list, item.id) if (children.length > 0) { item.children = children } // 将item项, 追加到arr数组中 arr.push(item) } }) return arr }我们发现,一级数据的pid值是空,所以传入的value为空,一级菜单就得到了.再以 item.id 作为 父 id, 接着往下找(递归),判断递归的数组长度,长度大于0则,是对应父级的children(不固定,element定义可以在props更改哦 记得看文档)效果如下将tree数据转为arrfunction treeToList(data) { let ret = []; const fn = (list) => { list.forEach(item => { ret.push({ ...item, children: undefined, pid: item.pid === '0' ? null : item.pid}); if (item.children) { fn(item.children); } }) return ret; } return fn(data); }精简后的写法//第二种 const treeToarr = (list=[])=>{ let arr =[] list.forEach(item=>{ arr.push({...item,children:undefined,pid:item.pid==0? null : item.pid}) if(item.children){ // arr.push(...treeToarr(item.children)) arr =arr.concat(treeToarr(item.children)) } }) return arr }树形数据搜索返回节点刷抖音刚好看到这个视频,觉得手痒就写来试试.const findTree = (data, label) => { let result = [] if (data.label.includes(label)) { result.push(data.label) return result } if (data.children && data.children.length > 0) { for (let i = 0; i < data.children.length; i++) { let res = findTree(data.children[i], label) if (res.length > 0) { result = result.concat(data.label, res) break } } } return result }
目标样式：首先先来看下我们拿到的返回数据：tableData: [ { productType: "纺织品", price: 123, productName: '男装上衣', amount: 20, operate_number: "20180831142020", price: "36.00", updateTime: "2018-08-31", operator: "小吴" },{ productType: "纺织品", price: 123, productName: '男装裤子', amount: 20, operate_number: "20180831142020", price: "36.00", updateTime: "2018-08-31", operator: "小吴" },{ productType: "饮料", price: 123, productName: '康师傅冰红茶', amount: 20, operate_number: "20180823142020", price: "36.00", updateTime: "2018-08-31", operator: "小吴" },{ productType: "纺织品", price: 223, productName: '男装裤子', amount: 10, operate_number: "20180821142020", price: "36.00", updateTime: "2018-08-31", operator: "小王" },{ productType: "绸缎", price: 888, productName: '旗袍', amount: 200, operate_number: "20180821142020", price: "36.00", updateTime: "2018-08-31", operator: "小吴" },{ productType: "绸缎", price: 123, productName: '席子', amount: 20, operate_number: "20180821142020", price: "36.00", updateTime: "2018-08-31", operator: "小吴" }, ] 我们需要将返回的数据按照该字段（operate_number）进行处理。—— 将相同operate_number的信息的索引进行对应存储。getOrderNumber() { let OrderObj = {} this.tableData.forEach((element, index) => { element.rowIndex = index if (OrderObj[element.operate_number]) { OrderObj[element.operate_number].push(index) } else { OrderObj[element.operate_number] = [] OrderObj[element.operate_number].push(index) } }) // 将数组长度大于1的值 存储到this.OrderIndexArr（也就是需要合并的项） for (let k in OrderObj) { if (OrderObj[k].length > 1) { this.OrderIndexArr.push(OrderObj[k]) } } }, 然后根据我们页面展示需要， 控制哪一些列上的数据是需要进行展示的。 // 合并单元格 objectSpanMethod({row,column,rowIndex,columnIndex}) { if (columnIndex === 0 || columnIndex === 3 || columnIndex === 4) { for (let i = 0; i < this.OrderIndexArr.length; i++) { let element = this.OrderIndexArr[i] for (let j = 0; j < element.length; j++) { let item = element[j] if (rowIndex == item) { if (j == 0) { return { rowspan: element.length, colspan: 1 } } else if (j != 0) { return { rowspan: 0, colspan: 0 } } } } } } }, 经过上述操作之后我们就能成功的绘制出如图所需要的效果了。##完整代码后面附上##存在问题不过这样的话会存在一个显示问题。就是当我们的鼠标滑过的时候，显示效果不佳，如下图所示：合并后的一行数据，滑过的时候效果就不对了，接下来我们就来解决一下这个问题，让效果能达到如下图所示关键词##: cell-mouse-enter cell-mouse-leave cell-class-name通过触发鼠标划入，划出的时候单元格的样式// 单元格样式 tableRowClassName({row,rowIndex}) { let arr = this.hoverOrderArr for (let i = 0; i < arr.length; i++) { if (rowIndex == arr[i]) { return 'hovered-row' } } },// 鼠标划入，划出效果 cellMouseEnter(row, column, cell, event) { this.rowIndex = row.rowIndex; this.hoverOrderArr = []; this.OrderIndexArr.forEach(element => { if (element.indexOf(this.rowIndex) >= 0) { this.hoverOrderArr = element } }) }, cellMouseLeave(row, column, cell, event) { this.rowIndex = '-1' this.hoverOrderArr = []; } 附上完整代码 element-ui实现合并单元格效果 <el-table ref="multipleTable" border :span-method="objectSpanMethod" :cell-class-name="tableRowClassName" @cell-mouse-leave="cellMouseLeave" @cell-mouse-enter="cellMouseEnter" :data="tableData" style="width: 80%;margin:0 auto;"> <el-table-column label="商品类别" align="center"> {{scope.row.productType}} </el-table-column> <el-table-column label="商品价格" align="center"> {{scope.row.price}} </el-table-column> <el-table-column label="商品名称" width="180px" align="center"> {{scope.row.productName}} </el-table-column> <el-table-column label="操作人员" align="center"> {{scope.row.operator}} </el-table-column> <el-table-column prop="updateTime" label="更新时间" align="center"> </el-table-column> </el-table> ————————————————版权声明：本文为CSDN博主「小白变怪兽」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。原文链接：https://blog.csdn.net/u013558749/article/details/82257168
:http-request="handleRequest" handleRequest(obj) { // console.log(obj) // 找到对应的上传的这个file的项 const fileObj = this.fileList.find(item => item.uid === obj.file.uid) fileObj.status = 'uploading'// 要上传的文件状态改为uploading if (obj.file) { // 执行上传操作 cos.putObject({ Bucket: 'luoxue-1252247442', /* 存储桶 */ Region: 'ap-nanjing', /* 存储桶所在地域，必须字段 */ Key: obj.file.name, /* 文件名 */ StorageClass: 'STANDARD', // 上传模式, 标准模式 Body: obj.file, // 上传文件对象 //进度的回调函数，进度回调响应对象（progressData） onProgress: (progressData) => { //percentage 百分比 fileObj.percentage = parseInt(progressData.percent * 100) } }, (err, data) => { // console.log(err || data) if (err) return console.log(err) if (data.statusCode === 200) { // console.log('地址:', 'https://' + data.Location) // console.log('uid:', obj.file.uid) // 更新状态 fileObj.status = 'success' // 修改地址 fileObj.url = 'https://' + data.Location } console.log(data) }) } },记得先导包,配置身份import COS from 'cos-js-sdk-v5' // 导入腾讯云的包(sdk) const cos = new COS({ SecretId: xxx, // 身份识别ID SecretKey: xxx // 身份秘钥 })腾讯对象存储文档地址:https://cloud.tencent.com/document/product/436/64960
问题如下:首先,elementui是有对应的事件函数的,如图:步骤1.页面代码 <el-upload :show-file-list="false" class="upload-demo" :before-upload="beforeUpload" :on-progress="onProgress" :on-success="onSuccess" :on-error="onError" action="/api/upload"> <el-button size="small" type="primary">点击上传</el-button> </el-upload> 2.在before-upload事件中判断文件大小，不符合返回falsebeforeUpload(file) { let fileObj = { name: file.name, size: file.size, status: "开始上传", process: 0, id: file.uid } if (file.size > 100 * 1024 * 1024) { console.log('上传文件过大', file.size) return false //必须返回false } this.fileList.push(fileObj) }, ————————————————版权声明：本文为CSDN博主「franklin_yuan」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。原文链接：https://blog.csdn.net/xin_yun_Jian/article/details/103620480补充3.发现这个兄弟询问的是上传总文件的大小,emmm.目前不知道他的需求是一次上传多个文件的总和,还是分多次上传文件的大小总和.可以定义一个变量,每次上传将上传文件的大小累加给这个变量.对这个变量进行判断但是这种有bug啊,页面一刷新定义的变量值就重置了...
1.配置externals结合CDN加速流程1.找到要CDN加速的包(report.html)中最大的包2.去jsdelivr(Boot等CDN加速)上搜索对应的包名3.将js文件引入到test.html测试,观察window对象中添加了什么属性4.再vue.config.js中配置 externals属性5.将测试的代码复制导index.html(注意全局 css也可以引入过来,将main.js的引入删除) configureWebpack: { 打包优化 只要配置了externals,webpack 不会打爆对应的第三方模块 项目中怎么找echarts呢? 由属性值来指定 echarts:'echarts' 意思就是不打包echarts 模块了,去 window中找echarts变量 externals: { // import 导包的包名:windows全局的成员名称 echarts: 'echarts' } }CDN文件配置以采用CDN的方式，在页面模板中预先引入vue.config.jsconst cdn = { css: [ 'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // element-ui css 样式表 ], js: [ // vue must at first! 'https://unpkg.com/vue/dist/vue.js', // vuejs 'https://unpkg.com/element-ui/lib/index.js', // element-ui js 'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/jszip.min.js', 'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/xlsx.full.min.js', 'https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.min.js' ] }但是，这时的配置实际上是对 开发环境 和 生产环境 都生效的，let externals = {} let cdn = { css: [], js: [] } const isProduction = process.env.NODE_ENV === 'production' // 判断是否是生产环境 if (isProduction) { externals = { // key(要排除的包名), value(引入的CDN包的全局变量名) 'vue': 'Vue', 'element-ui': 'ELEMENT', 'xlsx': 'XLSX', 'moment': 'moment' } cdn = { css: [ 'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // element-ui css 样式表 ], js: [ // vue must at first! 'https://unpkg.com/vue/dist/vue.js', // vuejs 'https://unpkg.com/element-ui/lib/index.js', // element-ui js 'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/jszip.min.js', 'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/xlsx.full.min.js', 'https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.min.js' ] } }底下使用 externalsconfigureWebpack: { // 配置单页应用程序的页面的标题 name: name, externals: externals, resolve: { alias: { '@': resolve('src') } } },注入 CDN文件变量 到模板chainWebpack(config) { // it can improve the speed of the first screen, it is recommended to turn on preload config.plugin('preload').tap(() => [ { rel: 'preload', // to ignore runtime.js // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171 fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/], include: 'initial' } ]) // 注入cdn变量 (打包时会执行) config.plugin('html').tap(args => { args[0].cdn = cdn // 配置cdn给插件 return args }) ... }找到 public/index.html。通过你配置的CDN Config 依次注入 css 和 js。 <% for(var css of htmlWebpackPlugin.options.cdn.css) { %> <link rel="stylesheet" href="<%=css%>"> <% } %> <% for(var js of htmlWebpackPlugin.options.cdn.js) { %> <script src="<%=js%>"> <% } %>最后，进行打包2.路由懒加载Vue路由懒加载首先,vue是一个SPA单页应用,只有一个html页面.不管分成多少个组件,在第一次加载的时候都会把所有js文件加载出来.哪怕在第一次加载的时候这个组件没显示,也还是会加载对应js文件.导致这个js文件过大,加载速度变慢,影响用户体验.所以,vue推出了路由懒加载.1.什么是路由懒加载仅在需要的时候,加载路由对应的组件页面2.好处可以提高index.html首页的打开速度Vue Router 支持开箱即用的动态导入，这意味着你可以用动态导入代替静态导入：// 将 // import UserDetails from './views/UserDetails' // 替换成 const UserDetails = () => import('./views/UserDetails') const router = createRouter({ // ... routes: [{ path: '/users/:id', component: UserDetails }], })可以简写哦routes: [{ path: '/users/:id', component: () => import('./views/UserDetails') }], 3.配置路由模式hash模式 ：使用 URL 的 hash 来模拟一个完整的 URL, 其显示的网路路径中会有 “#” 号。hash 虽然出现URL中，但不会被包含在HTTP请求中，对后端完全没有影响，因此改变hash后刷新, 也不会有问题hash模式示例: http://localhost:8080/#/home http://localhost:8080/#/user history模式： 美化后的hash模式，路径中不包含“#”。依赖于Html5 的 history api 。由于改变了地址, 刷新时会按照修改后的地址请求后端, 需要后端配置处理, 将地址访问做映射, 否则会404history模式示例: http://localhost:8080/home http://localhost:8080/user改成history模式非常简单，只需要将路由的mode类型改成history即可// 创建路由实例 const createRouter = () => new Router({ mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), // 指定路由规则 routes: [ ...constantRoutes // 静态路由, 首页 ] })
群聊面试题1.函数声明代码如下:setTimeout(function a(){ // debugger console.log(a) //f a() a= 8 console.log(a) //f a() }) 群友理解:个人理解:js的函数优先原则-在js函数声明和变量都会被提升，但是函数会优先提升，变量覆盖不了函数的a=8那里，是静默失败setTimeout(function a(){ // debugger console.log(a) //f a() a= 8 // 静默失败 console.log(a) //f a() }) // console.log(a) // function a(){ // debugger // console.log(a) //f a() // a= 8 // console.log(a) //f a() // } // a()2.margin 外边距塌陷问题2022-05-08T04:15:11.png这里就是外边距塌陷了,解决方法有很多.我让他加了个透明的border外边距塌陷出现的情况: 1.两个同级元素,上面的给margin-bottom 下面的给margin-top,他们的间距会重叠,以最大的那个计算 解决办法:两个外边距不同时出现 2.一对父子元素,内部的盒子给margin-top,欺负元素也会受到影响,也会发生外边距重叠.关于外边距塌陷的解决方案:1.给父盒子设置border 2.父盒子overflow:hidden 3.为父盒子设置padding 4.给父盒子设置position:fixed 5.给父盒子添加 display:table 6.利用为元素给父元素的的前面添加一个空元素 .father::before{ content:'', display:table; }防抖和节流函数防抖 函数防抖：单位时间内，频繁触发事件，只会触发最后一次函数防抖实际开发应用场景： 实时获取输入框文本小知识:王者荣耀的回城函数节流函数节流：单位时间内，频繁触发事件，只会触发一次函数节流应用场景 ： 解决高频事件，频繁触发事件浪费性能小知识:王者荣耀的技能类似封装// 防抖 const debounce = (fn, delay = 1000) => { let timer = null return function (...args) { clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, args) }, delay) fn } } // 节流 const throttle = (fn, delay = 1000) => { let timer = null return function (...args) { if (!timer) { timer = setTimeout(() => { fn.apply(this, args) timer = null }, delay) } } }1.什么是ref用来获取Dom的 在html标签中获取的就是当前的Dom元素 在Vue组件上获取的就是当前组件实例2.v-if和v-for的优先级v-for的优先级更高 是在源码中体现的 function genElement v-if不能和v-for一起使用，会造成性能浪费3.v-if和v-show的区别1.展现形式不同 v-if是创建一个dom节点 v-show是 display：none block 2.使用场景不同 初次加载v-if要比v-show好，页面不会做加载盒子 频繁切换v-show要比v-if好，创建和删除的开销太大了，显示隐藏开销小4.Vue生命周期相关1.有哪些生命周期系统自带 beforeCreate 组件实例被创建之初 created 组件实例已经完全创建 beforeMount 组件挂载之前 mounted 组件挂载到实例上去之后 beforeUpdate 组件数据发生变化，更新之前 updated 组件数据更新之后 beforeDestroy 组件实例销毁之前 destroyed 组件实例销毁之后(注销事件、清除定时器)2.一旦进入到页面或组件，会执行哪些生命周期？顺序beforeCreate created beforeMount mounted3.在哪个阶段有$el，在哪个阶段有$data？beforeCreate 啥也没有 created 有data，没有el beforeMount 有data，没有el mounted 都有4.如果加入了keep-alive会多两个生命周期activated deactivated5.如果加入keep-alive，第一次打开页面会执行哪些生命周期beforeCreate created beforeMount mounted activated6.如果加入keep-alive，第二次或n次打开页面会执行哪些生命周期只执行activated5.谈谈你对keep-alive的理解1.是什么vue系统自带的一个组件，功能：用来缓存组件的。提升性能2.使用场景就是用来缓存组件，提升项目的性能。具体实现形式：比如从首页进入到详情页。如果用户在首页每次点击都是相同的，那么详情页就没必要请求N次了，直接缓存起来就行了。如果用户点击的不是同一个，那么就直接请求。6.scoped原理1.作用：让样式在本组件中生效，不影响其他组件。 2.原理：给节点新增自定义属性，然后css根据属性选择器添加样式。7.nextTick是什么？(相当于一个定时器)获取更新后的dom内容(视图更新异步的) 格式 this.$nextTick(回调())什么时候用：当改变数据后，要基于更新后的新 DOM 进行某些操作时，要在 nextTick 所指定的回调函数中执行补充:判断环境优先微任务:防止多一次渲染原理:将传入的回调函数包装成异步任务，异步任务又分微任务和宏任务，为了尽快执行所以优先选择微任务；nextTick 提供了四种异步方法 Promise.then、MutationObserver、setImmediate、setTimeout(fn,0)8.父组件传值到子组件父组件: 子组件: props:['msg'] props:{ msg:数据类型 }9.子组件传值到父组件子组件： this.$emit('自定义事件名称'，要传的数据)； 父组件： methods:{ getVal(msg){ //msg就是,子组件传递的数据 } } Tips: // 父向子传值 1.自定义属性 2.this.$children // 子向父传值 1.自定义属性 2.this.$parent10.兄弟组件之间相互传值通过一个中转(bus) A兄弟传值: import bus from '@/common/bus' bus.$emit('toFooter',this.msg) B兄弟接收: import bus from '@/common/bus' bus.$on('toFooter,(data)=>{ this.str=data //data就是this.msg的数据 })11.computed、watch、methods有什么区别1.computed vs methods区别computed是有缓存的,methods没有2.computed vs watch区别watch是监听,数据或者路由发生了改变才可以响应(执行) computed计算某一个属性的改变,如果某一个值改变了,计算属性会检测到并返回 wacth是当前监听的数据改变了,才会执行内部代码12.promise有几种状态？pending(进行中) fulfilled(成功) reject(失败)13.find和filter的区别区别一:返回的内容不同 filter：返回的是新数组 find：返回具体的内容 区别二： find：匹配到第一个即返回 filter：返回整体(没一个匹配的都返回)14.some和every的区别some==>如果有一项匹配则返回true every==>全部匹配才返回true15.let const var的区别var、let、const 共同点都是可以声明变量的 区别一： var 具有变量提升的机制 let和const没有变量提升的机制 区别二: var 可以多次声明同一个变量 let和const不可以多次声明同一个变量 区别三： var、let声明变量的 const声明常量 var和let声明的变量可以再次赋值，但是const不可以再次赋值了。 区别四： var声明的变量没有自己的作用域 let const声明的变量有自己的作用域16.将下面对象进行合并const a={a:1,b:4} const b={b:2,c:3}方式一：Object.assignconst a = {a:1,b:4}; const b={b:2,c:3}； let obj1= Object.assign(a,b)； console.log(obj1);方式二：...展开运算符let obj2 ={...a,...b}; console.log(obj2);17.props和data的优先级props→methods→data→computed→watch18.Vuex有哪些属性？state、getters、mutations、actions、modules state 类似于组件中data，存放数据 getters 类型于组件中computed mutations 类似于组件中methods actions 提交mutations的 modules 把以上4个属性再细分，让仓库更好管理19.Vuex中的mutaitons和actions区别mutaitons：都是同步事物 actions：可以包含任意异步操作 使用区别:mutations中可以方入函数,actions也可以放入函数 但是一般我们在mutations中放入函数而actions是提交mutations20.Vuex如何做持久化存储Vuex本身不是持久化存储 1.使用localStorage自己写 2.使用vuex-persist插件 **什么场景用Vuex** 共享、方便管理、方便维护、组件传值 项目：购物车数据、订单数据、用户的登录信息21.Vue路由模式路由模式有俩种：history、hash 区别： 1.表现形态不同 history:http://localhost:8080/about hash:http://localhost:8086/#/about 2.跳转请求 history ： http://localhost:8080/id ===>发送请求 hash :不会发送请求 3.打包后前端自测要使用hash，如果使用history会出现空白页22.介绍一下SPA以及SPA有什么缺点SPA是什么？单页面应用 缺点： 1.SEO优化不好 2.性能不是特别好23.路由导航守卫有哪些全局、路由独享、组件内 1.全局 beforeEach、beforeResolve、afterEach 2.路由独享 beforeEnter 3.组件内 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave24. js动画和css3动画的区别1.兼容性方面css3兼容性要比js差一点,js大多时候不考虑兼容性2.代码方面css3要完成一些动画效果,代码会比较冗长.js代码要比较简单一点3.功能上css3可以完成一些简单的动画功能,js的动画功能性更强.事件冒泡触发子元素事件,所有父级元素的同名事件都会依次触发.25.什么是BFC?BFC:Block Formatting Context， 名为 "块级格式化上下文"。简单来说,它是一种属性,这种属性会影响着元素的定位以及与其兄弟元素之间的相互作用.布局规则 1、内部的 Box 会在垂直方向，一个接一个地放置 2、Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠 3、每个元素的 margin box 的左边， 与包含块 border box 的左边相接触(对于从左往 右的格式化，否则相反)。即使存在浮动也是如此 4、BFC 的区域不会与 float box 重叠 5、BFC 就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。反 之也如此 6、计算 BFC 的高度时，浮动元素也参与计算 哪些元素会生成 BFC： 1、根元素 2、float 属性不为 none 3、position 为 absolute 或 fixed 4、display 为 inline-block， table-cell， table-caption， flex， inline-flex 5、overflow 不为 visible BFC解决了什么问题1.使用Float脱离文档流，高度塌陷2.Margin边距重叠26. Vue 的父组件和子组件生命周期钩子函数执行顺序加载渲染过程：父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted子组件更新过程：父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated父组件更新过程：父 beforeUpdate -> 父 updated销毁过程：父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed27.new Vue()实例中,data 可以直接是一个对象,为什么在 vue 组件中,data 必须是一个函数呢?因为组件是可以复用的,避免组件被复用时,数据存在引用关系.28. 数组扁平化 const newArr = arr => [].concat(...arr.map(item => Array.isArray(item) ? newArr(item) : item)) console.log(newArr([1, [2, 3], [4, [5], 6], 7]));29.判断一个字符串中出现最多的字符和次数这里我主要用下标和对象去存字符以及添加一个属性`value`去存次数. 30.同步任务和异步任务同步任务：立即执行的任务，直接被主线程读取并执行异步任务：异步执行的任务，会交给任务队列去处理，而异步任务又分为宏任务和微任务宏任务script，setTimeout，setInternal，setImmeditate，T/O，UI rendering 微任务process，nextTick，promise.then()，object.observe，MutationObserver 执行顺序同步任务直接进入执行栈由主线程直接执行，而异步任务会先移交给WebAPIs去处理，当异步任务被触发会将其回调函数放入任务队列中。执行栈空，则访问微任务队列，当微任务队列空后再去访问宏任务队列，如果在执行宏任务时遇到了微任务，则将微任务放入微任务队列。微任务执行前提是执行栈为空，宏任务执行前提是微任务队列为空。31.vue-loader是什么?vue-loader:一个webpack的loader,用来解析和转换vue文件,提取其中的script style template把他们交给对应的loader去处理特性:允许vue的组件,每个部分使用对应的loader允许在vue文件中使用自定义模块何自定义的loader将style和template中引用的资源当作模块依赖处理,为每个这样的组件模拟出scope允许使用热重载使用场景:1.编译template将编译用作vue的template选项,url转换成webpack的模块请求2.调用预处理(sass less等等)3.允许热重载(热加载)32.v-model双向绑定原理通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{str}}),从而实现了数据双向绑定的原理.33.promise 的静态方法静态方法:将方法挂载到函数上,这个方法可以称作静态方法函数身上的方法与属性 叫静态成员promise.all() 所有的异步完成时,触发.thenpromise.race() 完成一个异步时,触发.thenpromise.all/race([promise1,promise2]).then(success,fail)相等点:都可以将多个promise实例包装成一个新的promise实例不同点:all需要两个promise都成功,而race只有一个成功就可以调用successpromise.reject() 等价=> new Promise((res,rej)=>rej())promise.resolve() 等价=> new Promise((res,rej)=>res())34.什么是作用域链作用域链：当代码在一个环境中执行时，会创建变量对象的一个作用域链，由子级作用域返回父级作用域中寻找变量，就叫做作用域链。作用域链中的下一个变量对象来自包含环境，也叫外部环境。而再下一个变量对象则来自下一个包含环境，一直延续到全局执行环境。全局执行环境的变量对象始终都是作用域链中的最后一个对象。作用域链前端始终都是当前执行的代码所在环境的变量对象，如果环境是函数，则将 其活动对象作为变量对象。35.如何延长作用域链作用域链是可以延长的。延长作用域链： 执行环境的类型只有两种，全局和局部（函数）。但是有些语句可以在作用域链的前端临时增加一个变量对象，该变量对象会在代码执行后被移除。具体来说就是执行这两个语句时，作用域链都会得到加强try - catch 语句的 catch 块：会创建一个新的变量对象，包含的是被抛出的错误对 象的声明。with 语句：with 语句会将指定的对象添加到作用域链中36.src 和 href 的区别是？src（source）指向外部资源的位置，指向的内容将会嵌入到文档中当前标签所在位置。在请求 src 资源时会将其指向的资源下载并应用到文档中，如 JavaScript 脚本，img 图片和 iframe 等元素。当浏览器解析到该元素时，会暂停其他资源的下载和处理，直到将该资源加载、编译、执行完毕，类似于将所指向资源嵌入当前标签内。href（hypertext reference/超文本引用）指向网络资源所在位置，建立和当前元素（锚点）或当前文档（ 链 接 ）之间的链接 ，如果我们在文档中添加那么浏览器会识别该文档为 CSS 文件，就会并行下载资源并且不会停止对当前文档的处理。37.自定义指令的钩子注册或获取全局指令。// 注册 Vue.directive('my-directive', { bind: function () {}, inserted: function () {}, update: function () {}, componentUpdated: function () {}, unbind: function () {} }) // 注册 (指令函数) Vue.directive('my-directive', function () { // 这里将会被 `bind` 和 `update` 调用 }) // getter，返回已注册的指令 var myDirective = Vue.directive('my-directive')注册自定义指令实例:export const imgerror = { // el 指令所在的元素 // binding 指令的相关信息对象, binding.value 指令的值 inserted(el, binding) { // console.log(el, bindings) el.onerror = function() { // console.log('图片加载失败了'), 设置备用图片地址 el.src = binding.value } } }38.Vue中Created操作DOM1.在created中操作dom的时候,是报错的,获取不到dom,这个时候实例Vue实例没有挂载.解决办法:Vue.$nextTick(回调函数来获取)39.$emit的返回值在vue中this.$emit(); 返回值是this40.for of 和for in Vue中 v-for 可以用 for of 遍历对象41.cookie 、localstorage 、 sessionstrorage 之间有什么区别？与服务器交互：cookie 是网站为了标示用户身份而储存在用户本地终端上的数据（通常经过加密）cookie 始终会在同源 http 请求头中携带（即使不需要），在浏览器和服务器间来回传递sessionStorage 和 localStorage 不会自动把数据发给服务器，仅在本地保存存储大小：cookie 数据根据不同浏览器限制，大小一般不能超过4ksessionStorage 和 localStorage 虽然也有存储大小的限制，但比cookie大得多，可以达到5M或更大有期时间：localStorage 存储持久数据，浏览器关闭后数据不丢失除非主动删除数据sessionStorage 数据在当前浏览器窗口关闭后自动删除cookie 设置的cookie过期时间之前一直有效，与浏览器是否关闭无关42.cookie和session 的区别1、cookie数据存放在客户的浏览器上，session数据放在服务器上。2、cookie不是很安全，别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。3、session会在一定时间内保存在服务器上。当访问增多，会比较占用你服务器的性能考虑到减轻服务器性能方面，应当使用COOKIE。4、单个cookie保存的数据不能超过4K，很多浏览器都限制一个站点最多保存20个cookie。5、所以个人建议：将登陆信息等重要信息存放为SESSION其他信息如果需要保留，可以放在COOKIE中43.跨域问题及解决方案什么是跨域**指的是浏览器不能执行其他网站的脚本，它是由浏览器的同源策略造成的,是浏览器对 javascript 施加的安全限制，防止他人恶意攻击网站解决方式 1、jsonp原理：动态创建一个 script 标签。利用 script 标签的 src 属性不受同源策略限制。因为所有的 src 属性和 href 属性都不受同源策略限制。可以请求第三方服务器数据内容。步骤:1.1）去创建一个 script 标签1.2）script 的 src 属性设置接口地址1.3）接口参数,必须要带一个自定义函数名要不然后台无法返回数据。1.4）通过定义函数名去接收后台返回数据 //去创建一个 script 标签 var script = document.createElement("script"); //script 的 src 属性设置接口地址 并带一个 callback 回调函数名称 script.src = "HTTP://127.0.0.1:8888/index.php?callback=jsonpCallback"; //插入到页面 document.head.appendChild(script); //通过定义函数名去接收后台返回数据 function jsonpCallback(data){ //注意 jsonp 返回的数据是 json 对象可以直接使用 //Ajax 取得数据是 json 字符串需要转换成 json 对象才可以使用。 }2、 CORS：跨域资源共享原理：服务器设置 Access-Control-Allow-OriginHTTP 响应头之后，浏览器将会允许跨域请求限制：浏览器需要支持 HTML5，可以支持 POST，PUT 等方法兼容 ie9 以上，需要后台设置Access-Control-Allow-Origin: * //允许所有域名访问，或者 Access-Control-Allow-Origin: HTTP://a.com //只允许所有域名访问3、代理服务器说明: 同源策略, 是浏览器的安全策略, 服务器于服务器之间, 没有跨域问题! 所以可以利用代理服务器转发请求!开发环境的跨域问题 (使用webpack代理服务器解决)配置 devServer 的 proxy 配置项module.exports = { devServer: { // 代理配置 proxy: { // 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制 '/api': { target: 'www.baidu.com', // 我们要代理请求的地址 // 路径重写 pathRewrite: { // 路径重写 localhost:8888/api/login => www.baidu.com/api/login '^/api': '' // 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做 } }, } } }生产环境的跨域问题 (使用 nginx 服务器代理)44. 什么是同源策略？所谓同源策略是浏览器的一种安全机制，来限制不同源的网站不能通信。同源就是域名、协议、端口一致。45. js的执行机制 / 事件循环机制 / event loopjs是一个单线程的, 如果有一些高耗时操作就会带来进程阻塞问题, 为了解决这个问题, js有两种任务执行模式同步模式和异步模式 同步模式下创建的同步任务是立即执行的异步模式下创建的异步任务分为宏任务与微任务两种所有任务可以分成两种，一种是同步任务（synchronous），另一种是异步任务（asynchronous）。同步任务指的是，在主线程上排队执行的任务，只有前一个任务执行完毕，才能执行后一个任务。异步任务指的是，不进入主线程、而进入"任务队列"（task queue）的任务，只有"任务队列"通知主线程，某个异步任务可以执行了，该任务才会进入主线程执行。ES6 规范中 宏任务(macro-task)是由宿主浏览器或node发起的, 而微任务 (Jobs) 由JS自身发起的宏任务: 包括整体代码 script，setTimeout，setInterval微任务：Promise.then(非 new Promise)，process.nextTick(node 中46.js的数据类型有哪些？js的数据类型分为基本数据类型（string、number、boolean、null、undefined、symbol，BigInt）复杂数据类型(object)基本数据类型的特点：直接存储在栈中的数据复杂数据类型的特点：存储的是该对象在栈中引用，真实的数据存放在堆内存里symbol符号是原始值，且符号实例是唯一、不可变的符号的用途是确保对象属性使用唯一标识符，不会发生属性冲突的危险BigInt 数据类型的目的是比Number数据类型支持的范围更大的整数值。在对大整数执行数学运算时，以任意精度表示整数的能力尤为重要。使用BigInt，整数溢出将不再是问题。47.谈谈你平时都用了哪些方法进行性能优化？减少http请求次数、打包压缩上线代码、使用懒加载、使用雪碧图、动态渲染组件、CDN加载包。48.知道lodash吗？它有哪些常见的API ？Lodash是一个一致性、模块化、高性能的 JavaScript 实用工具库。_.cloneDeep 深度拷贝_.reject 根据条件去除某个元素。_.drop(array, [n=1] ) 作用：将 array 中的前 n 个元素去掉，然后返回剩余的部分.49.告诉我如下结果 ({}).length // undefined ({} + {}).length //30 ([] + []).length //0 (function () {}).length //0({}).length // undefined 是因为对象{}没有length属性，所以是undefined解析:说明下原因：由于javascript是弱类型语言，加法运算间接调用了对象的toString()方法 其结果是字符串的相加，变成了”[object Object]”数一下发现一个{}的toString()有15个字符，所以字符相加，个数是15的倍数，同理，其他的都这样另外，直接这样做{}.toString() 你得到的是undefined, 通过使用变量的方式才可以呈现， 但在js内部，并不需要这一步操作，这就是js弱类型的特性，代码如下其中，数组是有length属性的，空数组的长度为0。如果函数内部是通过 arguments 调用参数，而没有实际定义参数的话， length 只会的得到 0 。var a = {}; console.log(a.toString().length); // 1550. 简单用递归实现1-100累加我另外写了一个数组累加的方式,主要是new Array(100),只有一个参数的话会生成这个参数长度的空数组,博客有介绍哦 //数组 const qwe = Array.from(new Array(100), (item, index) => index + 1).reduce((sum, item) => sum + item, 0) console.log(qwe) // 递归 function factorial(n) { if (n === 1) return 1 return n + factorial(n - 1) } console.log(factorial(100))51.async/await的用法async/await的作用是:以同步的方式执行异步的操作async:async 函数执行返回一个PRomiseawait:await后接Promise则会等待Promise返回结果,接普通函数执行接收返回结果.await只能在async中使用,但是浏览器调试中可单独使用52.Class的静态属性、继承静态属性:static关键字定义的变量属性,只能通过Class构造函数的属性的方式去访问继承:使用Child extends Father {},且配合super对象,完成继承53.Promise的then为什么可以链式调用?Promise的then方法返回一个新的Promise对象,保证了then方法可以进行链式调用54. Promise 实现每隔1秒输出1,2,3第一种 let arr = [1, 2, 3]; arr.reduce((pre, cur) => { return pre.then(() => { return new Promise(r => { setTimeout(() => { r(console.log(cur)) }, 1000); }) }) }, Promise.resolve()) 第二种 Promise.resolve() .then(() => new Promise((resolve => { setTimeout(() => { console.log(arr[0]); resolve() }, 1000) }))) .then(() => new Promise((resolve => { setTimeout(() => { console.log(arr[1]); resolve() }, 1000) }))) .then(() => new Promise((resolve => { setTimeout(() => { console.log(arr[2]) }, 1000) })))55.SymbolES6 引入新的原始数据类型Symbol，表示独一无二的值[1,2,3].map(parseInt)[1,2,3].map(parseInt) parseInt(元素,基数(可以理解为进制)) [1,2,3].map((item,index)=>{ return parseInt(item,index) }) parseInt(1,0)=>1(10进制去算) parseInt(2,1)=>NaN parseInt(3,2)=>NaN 二进制没有3这个数字56.清除浮动的方式单伪元素双伪元素隔墙法 (放一个空标签 clear:both)父元素 overflow:hidden给父元素设置高度(BFC)57.判断数组还是对象Array.isArray() object.prototype.toString.call()instanceofconstrcounttypeof58.http缓存机制强制缓存浏览器发送请求去服务器,服务器返回资源,会在我们的响应头设置一个cache-control,max-age没有过期就不用发送请求了,直接从本地缓存中拿数据cache-control:max-age协商缓存(对比缓存):协商缓存是一种服务端缓存策略浏览器第一次请求,服务器返回资源和资源标识 浏览器把返回资源存在本地中,后续请求 携带上本地资源标识本地缓存是最新的,服务器就不返回数据 让浏览器直接在缓存拿本地资源不是最新的话,服务器就返回最新的资源文件和资源标识59 剩余参数restarguments 伪数组 rest 是真数组(剩余参数)60.vue的内置组件内置组件keep-alivecomponentstransitontransition-grounpslotrouter-link router-view61.怎么让浏览器全部变成灰色css的滤镜 filter 62.Promise.all([...])中如果有个请求挂了,怎么解决堵塞Promise.all([...p(url)])中如果有个请求挂了,怎么解决堵塞 function P(url) { return new Promise((resolve, reject) => { axios .get(url) .then((res) => { resolve(res) }) .finally((res) => { resolve(res) }) .catch(err => err) }) }用finally无论成功还是失败都会执行,catch捕获这个错误63.vue组件通信方式 事件总线 EventBus Bus.$on Bus.$emit 清除监听 Bus.$off 父子组件通信 props emit 插槽 ref $parent $children vuex $attrs 隔代通信(只能往后台传值) 第一代 provide 后代用接收 inject
移动端常用组件库1.Cube UI2.Mint UI3.NutUI4.VantUI5.Bootstrap Vue6.VUXPC端常用UI组件库1.Element2.VIew UI3.Ant4.Material5.bootstrap可视化工具6.vue-element-admin7.Variant Form低代码表单，可视化设计8.Vuetify9.Naive UI10.vxe-table个人强推1.Tailwind CSS 2.svg插图element-ui基本使用 示例1.安装element-ui:npm i elelment-ui -S2.src/main.jsimport Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; // 引入ElementUI组件库 import 'element-ui/lib/theme-chalk/index.css'; // 引入ElementUI全部样式 Vue.config.productionTip = false Vue.use(ElementUI) // 使用ElementUI new Vue({ el:"#app", render: h => h(App), }) 3.src/App.vue <el-row> <el-button icon="el-icon-search" circle></el-button> <el-button type="primary" icon="el-icon-edit" circle></el-button> <el-button type="success" icon="el-icon-check" circle></el-button> <el-button type="info" icon="el-icon-message" circle></el-button> <el-button type="warning" icon="el-icon-star-off" circle></el-button> <el-button type="danger" icon="el-icon-delete" circle></el-button> </el-row> element-ui按需引入安装 babel-plugin-component npm i babel-plugin-component -D修改 babel-config-jsmodule.exports = { presets: [ '@vue/cli-plugin-babel/preset', ["@babel/preset-env", { "modules": false }] ], plugins: [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }3.src/main.js:import Vue from 'vue' import App from './App.vue' import { Button,Row } from 'element-ui' // 按需引入 Vue.config.productionTip = false Vue.component(Button.name, Button); Vue.component(Row.name, Row); /* 或写为 * Vue.use(Button) * Vue.use(Row) */ new Vue({ el:"#app", render: h => h(App), })Element 表单校验校验必要性在向后端发请求调用接口之前，我们需要对所要传递的参数进行验证，来把用户的错误扼杀在摇篮之中。校验内容● 内容不能为空（*）● 密码长度必须多少位● 手机号的格式要合规● 邮箱的格式要合规● ...校验方式● 不依赖于任何组件的验证 ○ 在做提交之前，自己把数据分析处理一下。● 基于具体组件的验证（不同的组件库，它的验证方式可能也各有不同基本步骤定义验证规则。data()中按格式定义规则在模板上做属性配置来应用规则（三个配置）给表单设置 rules 属性传入验证规则给表单设置model属性传入表单数据给表单项（Form-Item ）设置 prop 属性，其值为设置为需校验的字段名 3.手动兜底验证定义表单验证规则格式data() { return { rules: { // 字段名1：表示要验证的属性 // 值: 表示验证规则列表。它是一个数组，数组中的每一项表示一条规则。 // 数组中的多条规则会按顺序进行 字段名1: [ { 验证规则1 }, { 验证规则2 }, ], 字段名2: [ { 验证规则1 }, { 验证规则2 }, ], } } }示例 { required: true, message: '请输入验证码', trigger: 'blur' }, { pattern: /^\d{6}$/, message: '请输入合法的验证码', trigger: 'blur' }, { min: 6, max: 8, message: '长度为6-8位', trigger: 'blur' }实操 data () { return { // 表单验证规则，整体是一个对象 // 键：要验证的字段, 值：是一个数组，每一项就是一条规则 rules: { // 字段名：mobile就表示要验证的 属性 // 值: 是一个数组。数组中的每一项表示一条规则。 mobile: [ { required: true, message: '请输入手机号', trigger: 'blur' } ] } } },注意rules中的属性名与表单数据项中的属性名必须是一致的。 模板中的配置内容:给 el-form 组件绑定 model 为表单数据对象给 el-form 组件绑定 rules 属性配置验证规则给需要验证的表单项 el-form-item 绑定 prop 属性，注意：prop 属性需要指定表单对象中的数据名称<el-form label-width="80px" :model="form" :rules="rules"> <el-form-item label="手机号" prop="mobile"> <el-input v-model="form.mobile"></el-input> </el-form-item> <el-form-item label="密码" prop="code"> <el-input v-model="form.code"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="onSubmit">立即创建</el-button> <el-button @click="onCancel">取消</el-button> </el-form-item> </el-form>手动兜底验证element-ui的表单组件.validate(valid => { if(valid) { // 通过了验证 } else { // 验证失败 } })element-样式重置html, body { margin: 0; padding: 0; height: 100%; } // 全局重置 element-ui 组件库的基础样式 .el-input__inner, .el-button, .el-link, .el-menu-item, .el-submenu__title, .el-form-item__label { font-size: 12px; } // 重置菜单的基础样式 .el-menu.el-menu--horizontal { height: 60px; border-bottom: none; } // 重置卡片的基础样式 .el-card.is-always-shadow, .el-card.is-hover-shadow:focus, .el-card.is-hover-shadow:hover { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); font-size: 13px; }
1. 拼接字符串原const name = '小明'; const score = 59; let result = ''; if(score > 60){ result = `${name}的考试成绩及格`; }else{ result = `${name}的考试成绩不及格`; }改进:三元const name = '小明'; const score = 59; const result = `${name}${score > 60?'的考试成绩及格':'的考试成绩不及格'}`;精简:逻辑优化const result = `${name}的考试成绩${score > 60?'':'不'}及格`; 2.关于输入框非空的判断非空判断是任何项目都会遇到,直接上代码原if(value !== null && value !== undefined && value !== ''){ //... }改:ES6中新出的空值合并运算符if((value??'') !== ''){ //... }3.关于if中判断条件的吐槽原if( type == 1 || type == 2 || type == 3 || type == 4 || ){ //... } 改:ES6中数组实例方法includesconst condition = [1,2,3,4]; if( condition.includes(type) ){ //... }4.关于列表搜索原const a = [1,2,3,4,5]; const result = a.filter( item =>{ return item === 3 } )改:ES6中的findconst a = [1,2,3,4,5]; const result = a.find( item =>{ return item === 3 } )5.查询数组中某个对象的属性来获得该属性的下标原:抱歉,原代码我忘记了改:ES6中的findindex,找到返回该属性索引(下标),没找到返回-1 find返回的是值,没找到就是undefined6.求累加和原:各种for循环去累加改:大哥,ES6里面的reduce你是没见过吗?reduce((previousValue, currentValue, currentIndex) => { /* ... */ } ) reduce((累加器, 当前值, 下表) => { /* ... */ },初始值 ) list.reduce((sum, item) => sum + item.price, 0)7.关于合并数据原:合并两个数组，合并两个对象const a = [1,2,3]; const b = [1,5,6]; const c = a.concat(b);//[1,2,3,1,5,6] const obj1 = { a:1, } const obj2 = { b:1, } const obj = Object.assign({}, obj1, obj2);//{a:1,b:1} 改:ES6的扩展运算符、Set 对象允许你存储任何类型的唯一值，无论是原始值或者是对象引用const a = [1,2,3]; const b = [1,5,6]; const c = [...new Set([...a,...b])];//[1,2,3,5,6] const obj1 = { a:1, } const obj2 = { b:1, } const obj = {...obj1,...obj2};//{a:1,b:1}8.保留有效数字Number.toFixed(n) n保留几位 不写默认进行四舍六入五看情况，不包括小数部分