实战面试题

244 阅读17分钟

ElementUI -Upload 上传

  • action——必选参数,上传的地址
  • headers——设置上传的请求头部
  • with-credentials——支持发送 cookie 凭证信息
  • show-file-list——是否显示已上传文件列表
  • accept——接受上传的文件类型
  • on-preview——点击文件列表中已上传的文件时的钩子
  • on-remove——文件列表移除文件时的钩子
  • on-progress——文件上传时的钩子
  • on-change——文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
  • before-upload——上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
  • before-remove——删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
  • list-type——文件列表的类型(是否在选取文件后立即进行上传)
  • file-list——上传的文件列表
  • http-request——覆盖默认的上传行为,可以自定义上传的实现
  • limit——最大允许上传个数
  • on-exceed——文件超出个数限制时的钩子
  • clearFiles——清空已上传的文件列表(该方法不支持在 before-upload 中调用)
  • abort——取消上传请求
  • submit——手动上传文件列表

ElementUI-Tree 树形控件

  • empty-text——内容为空的时候展示的文本
  • node-key——每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
  • render-after-expand——是否在第一次展开某个树节点后才渲染其子节点
  • load——加载子树数据的方法,仅当 lazy 属性为true 时生效
  • render-content——树节点的内容区的渲染 Function
  • default-expand-all——是否默认展开所有节点
  • highlight-current——是否高亮当前选中节点,默认值是 false。
  • default-expand-all——是否默认展开所有节点
  • expand-on-click-node——是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。
  • check-on-click-node——是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
  • auto-expand-parent——展开子节点的时候是否自动展开父节点
  • default-expanded-keys——默认展开的节点的 key 的数组
  • current-node-key——当前选中的节点
  • show-checkbox——节点是否可被选择
  • check-strictly——在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
  • filter-node-method——对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
  • accordion——是否每次只打开一个同级树节点展开
  • lazy——是否懒加载子节点,需与 load 方法结合使用
  • draggable——是否开启拖拽节点功能
  • allow-drag——判断节点能否被拖拽
  • allow-drop——拖拽时判定目标节点能否被放置。type 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
  • isLeaf——指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效
  • updateKeyChildren——通过 keys 设置节点子元素,使用此方法必须设置 node-key 属性
  • getCheckedNodes——若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
  • setCheckedNodes——设置目前勾选的节点,使用此方法必须设置 node-key 属性
  • getCheckedKeys——若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
  • setCheckedKeys——通过 keys 设置目前勾选的节点,使用此方法必须设置 node-key 属性
  • setChecked——通过 key / data 设置某个节点的勾选状态,使用此方法必须设置 node-key 属性
  • getCurrentKey——获取当前被选中节点的 key,使用此方法必须设置 node-key 属性,若没有节点被选中则返回 null
  • getCurrentNode——获取当前被选中节点的 data,若没有节点被选中则返回 null
  • setCurrentKey——通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
  • setCurrentNode——通过 node 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
  • getNode——根据 data 或者 key 拿到 Tree 组件中的 node
  • node-click——节点被点击时的回调
  • node-contextmenu——当某一节点被鼠标右键点击时会触发该事件
  • check-change——节点选中状态发生变化时的回调
  • check——当复选框被点击的时候触发
  • current-change——当前选中节点变化时触发的事件
  • node-expand——节点被展开时触发的事件
  • node-collapse——节点被关闭时触发的事件

如何把vue2项目升级到vue3?

  1. 用vue-cli创建一个vue3项目,里面安装完相关基础依赖(vuex,router)等
  2. 将非业务功能(样式,eslint等)迁移到vue3项目,修改完相关报错
  3. 将所有业务模块迁移至vue3项目,同时取消所有页面路由引用,这一步很重要,先改动整个项目大体结构,不设计业务页面
  4. 一个个路由的放开,写一个页面改一个页面,如果某个页面涉及多个页面引用,可以尝试将某些页面内容清空后进行引用

微信小程序中如何加载大批量数据列表

微信小程序 SETDATA数据量过大的解决(以分页加载为例)

  • 通过设置数组下标(二维数组),实现每次只setData新加载的数据。
  • setData可以通过分批来对同一个data中的对象进行修改,而不是从新覆盖某个data中的对象。
  • freesion.com/article/692…

vue自定义实现文件上传(封装组件)

blog.csdn.net/luofei_crea…

<template>
  <el-upload
    class="upload-demo"
    ref="upload"
    multiple
    action="/"
    :on-preview="handlePreview"
    :on-remove="handleRemove"
    :on-change="fileLimit"
    :file-list="accessoryList"
    :http-request="submitAccessoryList"
    :on-success="upSuccessAccessory"
    :auto-upload="false"
  >
    <el-button slot="trigger" size="small" type="primary">请选择文件</el-button>
    <el-button
      style="margin-left: 10px"
      size="small"
      type="linear"
      @click="submitUpload"
      >上传</el-button
    >
    <el-button
      style="margin-left: 10px"
      size="small"
      type="default"
      @click="(e) => (accessoryList = [])"
      >取消上传</el-button
    >
    <span style="color: red">(温馨提示:单个文件不超过5M)</span>
  </el-upload>
</template>

<script>
import { uploadAttachment } from "@/api";

export default {
  props: {
    //上传附件action的url,如果是自定义调接口上传文件此参数可以不传
    action: {
      type: String,
      default: () => "/",
    },
  },
  data() {
    return {
      accessoryList: [], //附件列表
      fileVoList: [], //上传接口需要的附件列表
      callback: null
    };
  },
  methods: {
    //点击附件内容
    handlePreview(file) {
      console.log("file是:", file.name);
    },
    //附件移除
    handleRemove(file) {
      this.$message.warning(`已移除${file.name}`);
      const index = this.fileVoList.findIndex(
        (item) => item.fileName == file.name
      );
      this.fileVoList.splice(index, 1);
      this.$emit("makeSure", this.fileVoList);
    },
    //自定义上传,如果是传action这里可以删掉这个方法,同时也删掉 :http-request这个属性
    async submitAccessoryList(param) {
      try {
        let formData = new FormData();
        formData.append("file", param.file);
        const res = await uploadAttachment(formData);
        this.fileVoList.push(res.data);

        if (this.fileVoList.length === this.accessoryList.length && typeof this.callback === 'function') {
            this.callback();
        }
      } catch (e) {
        console.log("上传失败", e);
      }
    },
    //确定上传附件
    submitUpload() {
      this.test().then((res) => {
        if (res.code == 200) {
            this.callback = () => {
            	//这里是自定义附件列表的格式参数,因为我是需要这个样子的参数,所以重新格式化一下
                this.fileVoList = this.fileVoList.map((item) => {
                return {
                    fileName: item.name,
                    fileSize: item.size,
                    fileUrl: item.url,
                    id: item.id,
                };
                });
                this.$emit("makeSure", this.fileVoList); //这里调用父组件方法,是想把this.fileVoList传给父组件,然后在form表单当作参数传给后端
            }
        }
      });
    },
    //异步拿取附件数据
    test() {
      return new Promise((res, rej) => {
        if (this.accessoryList.length > 0) {
          this.$refs.upload.submit();
          res({ code: 200 });
        } else {
          this.$message.error('请选择上传文件')
          rej({ code: 500 });
        }
      });
    },
    //限制上传附件
    fileLimit(file, fileList) {
    const extension = file.name.substring(file.name.lastIndexOf('.') + 1);
  	const size = file.size / 1024 / 1024;
  	if(extension !== 'zip') {
  		this.$message.warning('只能传后缀名为.zip的压缩文件')
  		return
  	}
      if (size > 5) {
        this.$message.warning("文件大小不得超过5M");
        return;
      }
      this.accessoryList = fileList;
    },

    //附件上传成功
    upSuccessAccessory() {
      this.$message.success("附件上传成功");
    },
  },
};
</script>

<style>
.el-upload-list {
  width: 500px;
}
</style>

uniapp 开发小程序虚拟长列表万条数据不卡顿

blog.csdn.net/weixin_3894…

1. 页面滚动

虚拟列表只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染,以实现减少消耗,提高用户体验的技术。它是长列表的一种优化方案,性能良好。当数据体量极大时,使用虚拟列表,可以极大减少节点的渲染,体验丝滑。

2. 区域滚动

可滚动视图区域 scroll-view 用于区域滚动。需注意在webview渲染的页面中,区域滚动的性能不及页面滚动。
使用竖向滚动时,需要给 一个固定高度,通过 css 设置 height;使用横向滚动时,需要给添加white-space: nowrap;样式。

前端性能提升长列表优化解决方案

  • 分片渲染(通过浏览器事件环机制,也就是 EventLoop,分割渲染时间)
  • 虚拟列表(只渲染可视区域)

虚拟列表

虚拟列表其实是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。

假设有1万条记录需要同时渲染,我们屏幕的可见区域的高度为500px,而列表项的高度为50px,则此时我们在屏幕中最多只能看到10个列表项,那么在首次渲染的时候,我们只需加载10条即可。

虚拟列表如何实现

虚拟列表的实现,实际上就是在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发生时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。

  • 计算当前可视区域起始数据索引(startIndex)
  • 计算当前可视区域结束数据索引(endIndex)
  • 计算当前可视区域的数据,并渲染到页面中
  • 计算startIndex对应的数据在整个列表中的偏移位置startOffset并设置到列表上
  1. 【分片渲染】 启用使用API setTimeout 分片渲染 每次渲染50条,进入宏任务列队进行页面渲染以提高效率。
  2. 开发一个【虚拟列表】组件
  1. 列表项高度固定,且每个列表项高度相等
  2. 列表项高度固定不相等,但组件调用方可以明确的传入形如(index: number)=>number的getter方法去指定每个索引处列表项的高度
  3. 列表项高度不固定,随内容适应,且调用方无法确定具体高度

js实现文件上传

  1. 创建上传点击事件

/*
文件上传思路总结     
1. 给file表单注册onchange事件       * 当用户选择图片之后执行     
2. 获取用户选择的图片       * this.files[0]     
3. 创建FormData对象       * 只有FormData才可以上传文件     
4. 将图片添加到FormData对象中       * fd.append('参数名', this.files[0])     
5. 发送ajax请求       * 文件上传请求方法一定是post, 且请求参数为 FormData对象     
*/

<input ref="file" type="file" name="" style="display: none" @change="onChange" />
type:属性值决定上传文件的类型 
@change: input的值变化时触发 this.$refs.file.click(): 触发弹框,让其可以选择 
选择好要上传的文件后,点击打开,于此就会触发@change事件

2. ### 处理接收的文件

onChange() {
    // 获取上传后的文件信息
    const file = this.$refs.file?.files[0];
    // 获取到文件信息后,后边就是如何将文件信息上传到服务端
    // 这里说明,要根据后端接口需求进行响应的传递
    // 区别一,上传的是图片文件,后端接口需要的是base64格式的图片信息,此时需要将获取的file转成base64
    // 区别二,后端接口需要formdata格式文件,此时就要将file转成formdata格式发送给后端

    // 这里说明一下formdata格式上传
    // url 接口地址全地址
    this.uploadFile(file, url);
}

  1. 处理服务端上传

uploadFile(file, url) {
		// 处理文件转换成formData格式
		const formdata = new FormData();
		// 这里只是基本设置,对应接口需求设置响应的类型属性值
      	formdata.set('file', file);
      	formdata.set('Status', 0);

		// 接口调用
		let xml = new XMLHttpRequest();
		xml.open('POST', url, true) // 第三个值指定接口是否异步
		// 设置请求头信息
		xml.setRequestHeader(’token‘, token);
		// 监控上传进度
		xml.upload.onprogress  = this.onprogressEvent
		// 接口调用成功回调
		xml.onload = this.onloadEvent
		// 接口调用失败处理
		xml.onerror = this.onerrorEvent
	}
	onprogressEvent(e) {
		if (e.lengthComputable) {
			// 可以获取到实时的接口进度
        	this.realTimePercent = +parseInt((e.loaded / e.total) * 100);
      }
	}
	onloadEvent(e) {
		// 获取到接口调用成功后的返回数据
		const res = JSON.parse(e.currentTarget.response);
		...
	}
	onerrorEvent(e) {
		// 接口调用失败后的处理
		。。。
	}

Vue如何实现权限管理

一、权限管理

权限管理就是让不同的用户只能访问自己权限内的资源,有以下几种

  • 路由权限,用户登录后只能看到自己权限内的导航菜单,且只能访问自己权限内的路由地址
  • 视图权限,用户只能看到自己权限内的内容和按钮
  • 请求权限,越权请求将其拦截

二、控制权限

  • 接口权限

用户登录成功后可以得到一个token,将token存起来,通过axios请求拦截器进行拦截,请求头里要携带token

  • 按钮权限 1.用v-if判断,当如果页面很多的时候,每个页面都要获取用户权限role和路由表里的meta.btnUse,然后再做判断,比较繁琐 通过自定义指令进行按钮权限的判断

1.首先配置路由

{path: 'venueSetting',component: _import('venue/venueSetting'),name: '场馆设置',meta: {btnUse: ['admin', 'editor']} }, {path: 'fieldSetting',component: _import('venue/fieldSetting'),name: '场地设置',meta: {btnUse: ['admin']} }

2.自定义权限鉴定指令

const has = Vue.directive('has', {bind: function (el, binding, vnode) {let btnPermissionsArr = [];if(binding.value){btnPermissionsArr = Array.of(binding.value);}else{btnPermissionsArr = vnode.context.route.meta.btnPermissions;} if (!Vue.prototype._has(btnPermissionsArr)) {el.parentNode.removeChild(el); } } }); // 权限检查方法 Vue.prototype.$_has = function (value) {let isExist = false;// 获取用户按钮权限let btnUseStr = sessionStorage.getItem("btnUse");if (btnUseStr == undefined || btnUseStr == null) {return false;}if (value.indexOf(btnUseStr) > -1) {isExist = true; } return isExist; }; export {has}

在使用的按钮中引用v-has指令

<el-button @click='editClick' type="primary" v-has>编辑

  • 菜单权限(可以理解成将页面与路由进行解耦)

    菜单与路由分离,菜单由后端返回

1.前端定义路由信息

{name: "login",path: "/login",component: () => import("@/pages/Login.vue") }

name字段作为和后端返回的菜单的唯一标识

2.全局路由守卫里做判断

function hasPermission(router, accessMenu) {if (whiteList.indexOf(router.path) !== -1) {return true;}let menu = Util.getMenuByName(router.name, accessMenu);if (menu.name) {return true;}return false; }

Router.beforeEach(async (to, from, next) => {if (getToken()) {let userInfo = store.state.user.userInfo;if (!userInfo.name) {try {await store.dispatch("GetUserInfo")await store.dispatch('updateAccessMenu')if (to.path === '/login') {next({ name: 'home_index' })} else {next({ ...to, replace: true })}}catch (e) {if (whiteList.indexOf(to.path) !== -1) { next()} else {next('/login')}}} else {if (to.path === '/login') {next({ name: 'home_index' })} else {if (hasPermission(to, store.getters.accessMenu)) {Util.toDefaultPage(store.getters.accessMenu,to, routes, next); } else {next({ path: '/403',replace:true })}}}} else {if (whiteList.indexOf(to.path) !== -1) {next()} else {next('/login')}}let menu = Util.getMenuByName(to.name, store.getters.accessMenu);Util.title(menu.title); });

Router.afterEach((to) => {window.scrollTo(0, 0); });

每次路由跳转的时候都要判断权限,这里的判断也很简单,因为菜单的name与路由的name是对应的,后端返回的菜单是经过权限过滤的

根据路由name找不到对应的菜单,就表示用户有没权限访问

如果路由很多,可以应用初始化时,只挂载不需要权限控制的路由。拿到后端返回的菜单后,根据菜单与路由的对应关系,筛选出可访问的路由,通过addRoutes动态挂载

  • 路由权限

在路由初始化的时候挂载全部路由,在路由上标记相应的权限信息,当路由跳转的时候做校验

const router = [{path: 'home',component: () => import('@/views/home'),name: 'homePage',meta: {title: '主页',roles: ['admin','editor'] } }]

初始化的时候先挂载不需要权限控制的路由,例如登录页。如果用户通过URL访问,则会跳转到404页面

登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由

function hasPermission(roles, permissionRoles) {if (roles.indexOf('admin') >= 0) return trueif (!permissionRoles) return truereturn roles.some(role => permissionRoles.indexOf(role) >= 0) }

const whiteList = ['/login', '/getMenuLis']

router.beforeEach((to, from, next) => {if (getToken()) { if (to.path === '/login') {next({ path: '/' }) } else {if (store.getters.roles.length === 0) {store.dispatch('GetUserInfo').then(res => {const roles = res.data.roles store.dispatch('GetRoutes', { roles }).then(() => { router.addRoutes(store.getters.addRouters)next({ ...to, replace: true })})}).catch((err) => {store.dispatch('logOut').then(() => {Message.error(err)next({ path: '/' })})})} else {if (hasPermission(store.getters.roles, to.meta.roles)) {next()} else {next({ path: '/401', replace: true, query: { noGoBack: true }}) }}}} else {if (whiteList.indexOf(to.path) !== -1) {next()} else {next('/login')}} })

大文件上传

  1. 对大文件做切片(使用File对象的slice方法进行分片成Blob格式-二进制)
  2. 通知服务器合并切片(传完之后再去组合分片)
  3. 控制多个请求的并发量
  4. 做断点传

分片上传

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(Part)来进行分片上传

上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件

大致流程如下:

  1. 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  2. 初始化一个分片上传任务,返回本次分片上传唯一标识;
  3. 然后借助 http 的可并发性,同时上传多个切片
  4. 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件

断点续传

断点续传指的是在下载或上传时,将下载或上传任务人为的划分为几个部分

每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度

一般实现方式有两种:

  • 服务器端返回,告知从哪开始
  • 浏览器端自行处理

上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可

如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可

使用场景

  • 大文件加速上传:当文件大小超过预期大小时,使用分片上传可实现并行上传多个 Part, 以加快上传速度
  • 网络环境较差:建议使用分片上传。当出现上传失败的时候,仅需重传失败的Part
  • 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用中比较常见

图片转换成base64格式的优缺点

  1. 优点

(1)base64格式的图片是文本格式,占用内存小,转换后的大小比例大概为1/3,降低了资源服务器的消耗;

(2)网页中使用base64格式的图片时,不用再请求服务器调用图片资源,减少了服务器访问次数。

(3)base64编码的字符串,更适合不同平台、不同语言的传输;

(4)算法是编码, 不是压缩, 编码后只会增加字节数,但是算法简单, 几乎不会影响效率,算法可逆, 解码很方便, 不用于私密信息通信;

(5)解码方便, 但毕竟编码了, 肉眼还是不能直接看出原始内容;

  1. 缺点

(1)base64格式的文本内容较多,存储在数据库中增大了数据库服务器的压力;

(2)网页加载图片虽然不用访问服务器了,但因为base64格式的内容太多,所以加载网页的速度会降低,可能会影响用户的体验。

(3)base64无法缓存,要缓存只能缓存包含base64的文件,比如js或者css,这比直接缓存图片要差很多,而且一般HTML改动比较频繁,所以等同于得不到缓存效益。

有没有单独封装过组件?(common,view)

答:有封装过,在项目的common文件夹中会存放项目的公用组件即可重用的部分放在common中,如项目的头组件,底部组件,侧边栏组件、导航栏组件等,项目里的view文件夹下存放与路由器绑定的组件。component一般存放重用的小组件(轮播图、分页器、模态框这些功能组件),view里存放页面级组件。

把这些页面重复的部分抽离出来进度单独的封装,有效减少代码量,提升了项目的开发效率。解决了传统项目中效率低、难维护、复用性低等问题。

  • 封装组件的目的就是为了解耦。
  • 通用组件必须具备高性能、低耦合的特性

vue的diff算法理解?

1)diff算法的作用: 用来修改dom的一小段,不会引起dom树的重绘

2)diff算法的实现原理: diff算法将virtual dom的某个节点数据改变后生成的新的vnode与旧节点进行比较,并替换为新的节点,具体过程就是调用patch方法,比较新旧节点,一边比较一边给真实的dom打补丁进行替换

3)具体过程详解:

a、在采用diff算法进行新旧节点进行比较的时候,比较是按照在同级进行比较的,不会进行跨级比较:

b、当数据发生改变的时候,set方法会调用dep.notify通知所有的订阅者watcher,订阅者会调用patch函数给响应的dom进行打补丁,从而更新真实的视图

c、patch函数接受两个参数,第一个是旧节点,第二个是新节点,首先判断两个节点是否值得比较,值得比较则执行patchVnode函数,不值得比较则直接将旧节点替换为新节点。如果两个节点一样就直接检查对应的子节点,如果子节点不一样就说明整个子节点全部改变不再往下对比直接进行新旧节点的整体替换

d、patchVnode函数:找到真实的dom元素;判断新旧节点是否指向同一个对象,如果是就直接返回;如果新旧节点都有文本节点,那么直接将新的文本节点赋值给dom元素并且更新旧的节点为新的节点;如果旧节点有子节点而新节点没有,则直接删除dom元素中的子节点;如果旧节点没有子节点,新节点有子节点,那么直接将新节点中的子节点更新到dom中;如果两者都有子节点,那么继续调用函数updateChildren

e、updateChildren函数:抽离出新旧节点的所有子节点,并且设置新旧节点的开始指针和结束指针,然后进行两辆比较,从而更新dom(调整顺序或者插入新的内容 结束后删掉多余的内容)

实现一个div,左边固定div宽度200px,右边div自适应

<div class= "container">
    <div class="left"></div>
    <div class="rigth"></div>
</div>
 
<style>
/*方法一: BFC(块级格式化上下文)*/
    .container{
        width:1000px;
        height:400px;
        border: 1px solid red;
    }
    .left{
        width:200px;
        height:100%;
        background: gray;
        float: left;
    }
    .rigth{
        overflow:hidden;  /* 触发bfc */
        background: green;
    }
 
/*方法二: flex布局 */
    .container{
        width:1000px;
        height:400px;
        border:1px solid red;
        display:flex;         /*flex布局*/
    }
    .left{
        width:200px; height:100%;background:gray;
        flex:none;
    }
    .right{
        height:100%;
        background:green;
        flex:1;        /*flex布局*/
    }
 
/* 方法三: table布局 */
    .container{
        width:1000px;
        height:400px;
        border:1px solid red;
        display:table;         /*table布局*/
    }
    .left{
        width:200px; 
        height:100%;
        background:gray;
        display:table-cell;
    }
    .right{
        height:100%;
        background:green;
        display: table-cell;
    }
 
/*方法四: css计算宽度calc*/
    .container{
        width:1000px;
        height:400px;
        border:1px solid red;
    }
    .left{
        width:200px;
        height:100%;
        background:gray;
        float:left;
    }
    .right{
        height:100%;
        background:green;
        float:right;
        width:calc(100% - 200px);
    }
   /* 方法五:左浮动+margin-left*/
   .container{
        width:1000px;
        height:400px;
        border:1px solid red;
    }
    .left{
        width:200px;
        height:100%;
        background:gray;
        float:left;
    }
    .right{
        height:100%;
        background:green;
       margin-left:200px;
    }
</style>

微前端既可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性,相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。 (micro-appsingle-spaqiankun) 它主要解决了两个问题:

  • 1、随着项目迭代应用越来越庞大,难以维护。
  • 2、跨团队或跨部门协作开发项目导致效率低下的问题。

element 组件的二次封装

首先,我们需要使用到 Vue API 中的两大高级应用属性

  • vm.$attrs
  • vm.$listeners
  • 在组件上使用 v-model

自定义事件也可以用于创建支持 v-model 的自定义输入组件。记住:

<input v-model="searchText">

等价于:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

当用在组件上时,v-model 则会这样:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

你在前端开发中遇到的最大挑战是什么,你是如何解决的?

  • 在自己开发的响应式网站中,如何确保网站在不同屏幕和设备中都能提供一致的用户体验和兼容性(挑战描述)。
  • 为了解决这问题,我通过浏览器开发者工具和真实设备进行测试以准确地识别出兼容问题,然后研究相关的标准和技术(flex和grid布局)确保元素在不同屏幕尺寸下排列和对齐,还有一些其他的库来检测是否能支持一些特性帮助我来完善一些兼容问题(解决方法)
  • 通过这些努力提高了网站的兼容性显著改善跨设备的用户体验,在这过程中不仅提升了自己对响应式和现代css技术的理解,还学会了如何有效地进行跨浏览器测试和调试。(结果和收获)
  • 总的来说,面对挑战并且找到解决方案的过程对我来说是一次宝贵的经历,我相信无论之后遇到多大的困难只要有正确的方法和坚持不懈的努力总能去解决这些问题的(收尾)