微信开放文档
前端A端、B端和C端是什么意思
-
A端 (Admin端/管理端): A端通常指的是后台管理系统或管理界面,是供系统管理员、内容管理者或者开发运维人员使用的界面。这部分用户通常需要管理平台数据、监控系统状态、配置系统设置或处理用户反馈等高级权限操作。例如,网站的后台管理系统,让管理员可以管理用户账号、编辑网站内容、查看统计数据等。
-
B端 (Business端/企业端): B端针对的是企业用户或商家,涉及企业间交易、企业内部管理、工作效率提升等方面的产品或服务。这类产品主要服务于企业的业务流程优化、资源管理、客户关系管理(CRM)、供应链管理等需求。比如,企业资源规划(ERP)系统、客户管理系统、办公自动化软件等都属于B端产品。
-
C端 (Consumer端/客户端): C端则是面向广大消费者或终端用户的,强调用户体验和个人需求满足。这包括了各种直接面向消费者的移动应用、网站、在线购物平台、社交软件、娱乐应用等。C端产品设计更注重界面友好性、操作便捷性和个性化体验,以吸引和留住用户。
总结来说,A端关注于系统的管理和维护,B端着重于企业级应用和服务,而C端则聚焦于个人消费者的使用体验和需求。在实际开发中,了解这些不同端点的特性和目标用户,有助于前端开发者更好地设计和实现产品功能。
说一下 HTTP 和 HTTPS 的区别
HTTPS:使用SSL(安全套接层)或TLS(传输层安全)加密协议来保护数据传输的安全性和机密性,以防止未经授权的访问和窃听。HTTPS协议通常用于处理敏感信息,如在线支付或登录凭证等。可以通过URL的前缀来识别一个网站是否使用了HTTPS协议,即使用了“https://”前缀而不是“http://”。
Vue前端面试总结
设计一个包含权限管理、权限分配、按钮级权限控制、全局加载管理、数据可视化、CSV文件导入导出以及处理10万量级数据优化的Vue3项目,需要综合运用Vue3的新特性、Pinia作为状态管理工具、以及TypeScript进行类型安全编程。
1. 项目结构与技术栈
- 前端框架: Vue3 (Composition API)
- 状态管理: Pinia
- 类型系统: TypeScript
- UI库: 可选Element Plus或Vuetify等,根据设计需求选择
- 数据可视化: ECharts或D3.js
- CSV处理: Papa Parse或其他TS支持的库
- 性能优化: Vue的Suspense, lazy loading, 分页等技术处理大量数据
2. 权限管理与分配
设计思路:
- 使用角色-权限模型,用户通过分配角色获得权限。
- 在Pinia中创建
authStore
来管理用户登录状态、角色信息和权限列表。 - 按钮级权限控制可以通过在Vue组件中使用计算属性或getter从store获取当前用户是否有操作某按钮的权限。
实现步骤:
- 定义权限模型: 使用TypeScript接口定义权限对象和角色对象。
- 初始化权限数据: 在后端获取用户角色和权限信息,并存储在Pinia store中。
- 权限检查指令: 创建自定义Vue指令,如
v-hasPermission="permissionKey"
,在模板中控制元素显示。
3. 全局Loading管理
- 利用Vue的
<Suspense>
组件包裹可能触发异步加载的内容,配合async/await
处理请求,实现更优雅的加载状态管理。 - 创建一个
loadingStore
来全局管理加载状态,包括是否显示全局加载提示。
4. 数据可视化
- 选择ECharts或D3等库,基于其提供的TypeScript支持进行图表开发。
- 在Vue组件中引入图表库,根据后端数据动态生成图表。
- 对于大数据量的图表展示,考虑使用分页加载数据或者数据采样技术减少前端渲染压力。
5. CSV文件导入导出
- 导入: 使用Papa Parse读取CSV文件内容,转换为JSON格式,再提交给后端处理或直接在前端处理(如数据预览)。
- 导出: 收集需要导出的数据,利用Blob对象和
FileSaver.js
或浏览器的URL.createObjectURL
方法生成并下载CSV文件。
6. 处理10万量级数据优化
- 分页: 不一次性加载所有数据,而是按需分页加载。
- 懒加载: 对于列表项或复杂组件使用Vue的lazy/suspense特性延迟加载。
- 服务器端渲染: 对于首屏数据,可以考虑使用SSR减少客户端渲染压力。
- 数据缓存: 利用浏览器缓存机制或Pinia的state持久化减少重复请求。
- 性能监控: 使用Chrome DevTools等工具定期审查性能,优化代码和资源加载策略。
结论
设计这样一个系统需要综合考虑前后端分离、数据流管理、用户体验和性能优化等多个方面。利用Vue3的最新特性和TypeScript的静态类型检查,结合Pinia的状态管理能力,可以构建出既强大又易于维护的权限管理系统。同时,合理运用各种库和技术手段处理大量数据和提升用户体验是关键。
1.请简述Vue.js的生命周期函数及其执行顺序。
2.Vue.js中的v-bind指令和v-mode】指令有什么区别?
3.请简述Vue.js的组件通信方式及其优缺点。
4.Vue.js如何实现父子组件之间的数据传递?
5.请简述Vue.js中的响应式原理。
6.如何在Vue.js中实现路由跳转?
7.Vue.js中的computed和watch有什么区别?
8.Vue.js中的v-fon指令和v-if指令有什么区别?
9.请简述Vue.js中的mixins和extends的作用及其区别。
10.Vue.js中的keep-alive组件有什么作用?如何使用?
1. **Vue.js的生命周期函数及其执行顺序**:
Vue.js的生命周期分为几个主要阶段,每个阶段都有对应的钩子函数,这些钩子函数按照以下顺序执行(简化版,基于Options API):
- **创建期间**:
- `beforeCreate`: 实例刚被创建,属性和方法还没有初始化。
- `created`: 实例已创建完成,属性和方法已被初始化,但DOM还未生成,$el属性不可用。
- `beforeMount`: 模板编译完成,即将挂载到DOM上。
- `mounted`: 实例已挂载到DOM上,这时可以访问DOM元素。
- **更新期间**:
- `beforeUpdate`: 数据变化导致的虚拟DOM重新渲染之前调用。
- `updated`: 数据变化导致的DOM更新完成后调用。
- **销毁期间**:
- `beforeUnmount`: 实例即将被卸载之前调用。
- `unmounted`: 实例已经被卸载,相关的一切都被清理完毕。
还有其他几个较为特殊的生命周期函数,但在日常开发中较少用到:`activated`, `deactivated`, `errorCaptured`, `serverPrefetch`等。
2. **Vue.js中的v-bind指令和v-model指令的区别**:
- **v-bind**: 用于动态地绑定一个或多个属性,或一个对象的属性到表达式。它使你能够将数据属性的值与HTML元素的属性绑定,当数据改变时,绑定的属性也会相应更新。
- **v-model**: 是一种双向数据绑定,常用于表单输入控件,如input、textarea等。它既能将数据模型的值绑定到输入框的值,也能在用户输入时更新数据模型。v-model本质上是v-bind和v-on的语法糖,简化了数据同步的过程。
3. **Vue.js的组件通信方式及其优缺点**:
- **Props/Events**: 父组件通过props向下传递数据给子组件,子组件通过$emit触发事件向上通知父组件。优点是简单直观,易于理解和维护;缺点是当组件层级深时,传递和管理变得复杂。
- **Vuex**: 使用全局状态管理器Vuex,适合跨组件、跨页面共享状态。优点是集中管理状态,易于追踪状态变化;缺点是引入了额外的概念和代码量,对小项目可能过度设计。
- **Event Bus**: 创建一个全局的事件中心,用于非父子关系的组件间通信。优点是灵活,易于实现;缺点是滥用可能导致难以维护的状态管理。
- **Provide/Inject**: 主要用于祖先组件向后代注入数据,不推荐用于常规数据传递,更多用于如插件注入等场景。优点是可以跨越多级传递;缺点是限制了组件的可重用性和可测试性。
4. **Vue.js如何实现父子组件之间的数据传递**:
- **Props**: 父组件通过props属性将数据传递给子组件,子组件通过props选项声明接收。
- **Custom Events**: 子组件通过`$emit`触发一个自定义事件,父组件在使用子组件的地方监听这个事件并作出相应处理。
5. **Vue.js中的响应式原理**:
Vue使用了数据劫持结合发布-订阅模式的方式来实现数据的响应式。在Vue实例初始化时,会遍历data选项中的所有属性,并使用`Object.defineProperty`来为每个属性添加getter和setter。当数据发生变化时,setter会被触发,Vue会记录此依赖关系,并通知相关联的Watcher对象进行更新,最终更新DOM。
6. **如何在Vue.js中实现路由跳转**:
Vue.js中通常使用Vue Router库来实现路由跳转。基本步骤包括安装Vue Router,定义路由,然后在Vue实例中创建router实例并挂载。跳转的方式有几种:
- 使用`<router-link>`组件进行导航。
- 在JavaScript中使用`this.$router.push()`或`this.$router.replace()`方法进行编程式导航。
7. **Vue.js中的computed和watch的区别**:
- **Computed**: 计算属性,基于依赖项进行缓存,只有当依赖的数据变化时才会重新计算。适用于衍生状态,即可以根据其他属性计算得出的值。
- **Watch**: 监听器,可以监听Vue实例上的数据变动,并在数据变化时执行回调函数。适合执行异步操作或开销较大的操作。
8. **Vue.js中的v-for和v-if指令的区别**:
- **v-for**: 用于循环遍历数组、对象等数据,并渲染列表。优先级高于v-if,如果同时存在,v-if将作为每个循环元素的条件渲染。
- **v-if**: 用于条件性地渲染一块内容,只有当条件为真时才会渲染。在频繁切换时有较高的切换成本,因为涉及DOM的销毁和重建。
9. **Vue.js中的mixins和extends的作用及其区别**:
- **mixins**: 混入,用于分发Vue组件中的可复用功能。一个混入对象可以包含任意组件选项,当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
- **extends**: 扩展,允许一个Vue组件继承另一个组件的选项,类似于JavaScript中的原型继承。与mixins不同,extends用于实现组件间更直接的继承关系,而不是功能的混入。
10. **Vue.js中的keep-alive组件的作用及使用**:
- **作用**: `keep-alive`是一个抽象组件,用于缓存不活动的组件实例,而不是销毁它们。主要用于保留组件状态或避免重复渲染性能开销大的组件。
- **使用**: 将`<keep-alive>`包裹在路由视图组件(`<router-view>`)或其他动态组件上,可以缓存这些组件的实例。还可以通过`include`和`exclude`属性来指定需要缓存或不缓存的组件名称。
vue数组操作方法
Vue.js 中处理数组时,可以使用JavaScript原生的数组方法,但为了确保Vue能够追踪这些变化并自动更新DOM,有时需要特别注意。以下是一些Vue中常用的数组操作方法及其使用注意事项:
基础操作方法
-
push: 在数组末尾添加一个或多个元素。
Javascript1myArray.push('newElement');
-
pop: 移除数组最后一个元素并返回它。
Javascript1let lastElement = myArray.pop();
-
shift: 移除数组第一个元素并返回它。
Javascript1let firstElement = myArray.shift();
-
unshift: 在数组开头添加一个或多个元素。
Javascript1myArray.unshift('newElement');
-
splice: 可以用来插入、删除或替换数组中的元素。
Javascript1// 删除起始索引为1的元素 2myArray.splice(1, 1); 3// 从索引1开始插入元素 4myArray.splice(1, 0, 'newElement');
-
concat: 返回两个或更多数组的合并副本,不改变原数组。
Javascript1let newArray = myArray.concat(anotherArray);
高级迭代方法
这些方法都不会改变原始数组,而是返回一个新数组。
-
map: 对数组中的每个元素执行一个函数,并返回一个新的数组。
Javascript1let newArray = myArray.map(item => item * 2);
-
filter: 创建一个新数组,其包含通过测试的所有元素。
Javascript1let newArray = myArray.filter(item => item > 10);
-
forEach: 对数组的每个元素执行一个函数,没有返回值。
Javascript1myArray.forEach(item => console.log(item));
-
find 和 findIndex: 返回符合测试条件的第一个元素或其索引。
Javascript1let item = myArray.find(element => element.id === 1); 2let index = myArray.findIndex(element => element.id === 1);
-
some 和 every: 检查数组中是否有元素满足(some)或所有元素都满足(every)提供的测试函数。
Javascript1let hasEven = myArray.some(num => num % 2 === 0); 2let allPositive = myArray.every(num => num > 0);
-
reduce: 对数组元素进行累积,最终生成单个值。
Javascript1let sum = myArray.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
Vue特有注意事项
- 使用以上方法时,Vue通常能自动侦测到数组的更改并更新视图。但对于修改数组长度或直接设置索引值等操作,Vue可能无法自动追踪。这时应使用
Vue.set
或this.$set
(在Vue 2中)来确保响应式: Javascript1this.$set(myArray, index, newValue);
- 在Vue 3中,如果你使用
ref
或reactive
来创建响应式数据,直接修改它们通常是响应式的,但如果在复杂操作中遇到问题,可以考虑使用Vue.set
的等价物。
记住,理解Vue的响应式机制对于高效地操作数组非常重要。在实际开发中,结合具体场景选择最合适的方法。
路由分配和路由生命周期
路由分配和路由生命周期是前端开发中的重要概念,特别是在单页面应用程序(SPA)框架中,如Vue、React或Angular等,它们管理着页面或组件如何根据URL的变化而加载、显示、更新和销毁。下面分别解释这两个概念:
路由分配
路由分配指的是将特定的URL路径映射到应用内部的一个可渲染组件或页面的过程。这个过程让应用能够根据用户请求的URL展示相应的内容,而无需重新加载整个页面。在现代前端框架中,路由通常通过路由配置来实现,这些配置定义了不同的URL路径与应该渲染的组件之间的映射关系。
例如,在Vue中,你可以使用Vue Router来定义路由:
Javascript
1// Vue Router的基本配置示例
2import Vue from 'vue';
3import Router from 'vue-router';
4import HomeComponent from './components/HomeComponent.vue';
5import AboutComponent from './components/AboutComponent.vue';
6
7Vue.use(Router);
8
9export default new Router({
10 routes: [
11 { path: '/', component: HomeComponent },
12 { path: '/about', component: AboutComponent }
13 ]
14});
在这个例子中,当用户访问 '/'
时,HomeComponent
将被渲染;访问 '/about'
时,则渲染 AboutComponent
。
路由生命周期
路由生命周期是指组件随路由变化而经历的一系列状态变化。在不同的框架中,生命周期钩子的名称和功能可能有所不同,但基本原理相似,都是围绕组件的创建、渲染、更新和销毁过程。主要生命周期钩子包括但不限于:
-
加载/进入页面时:
- Vue中的
beforeRouteEnter
,beforeRouteUpdate
,mounted
,activated
(对于keep-alive的组件)等。 - React中的
componentDidMount
,getInitialProps
(Next.js中用于服务器端渲染)。
- Vue中的
-
页面更新时:
- Vue中的
beforeRouteUpdate
,updated
。 - React中的
componentDidUpdate
。
- Vue中的
-
离开页面时:
- Vue中的
beforeRouteLeave
,deactivated
。 - React中的
componentWillUnmount
。
- Vue中的
-
路由守卫:
- Vue Router提供了全局守卫(
beforeEach
,beforeResolve
,afterEach
)和组件内守卫(如上述的beforeRouteEnter
,beforeRouteUpdate
,beforeRouteLeave
),可以在路由切换前后执行逻辑。
- Vue Router提供了全局守卫(
路由生命周期的关键在于理解组件如何随着用户导航行为的变化而被创建、激活、更新或销毁,以及如何在这些关键时刻执行特定的操作,比如获取数据、清理资源或执行过渡动画等。
VUE权限分配
在Vue应用中实现权限分配通常涉及几个核心步骤和概念,以下是基于常见的RBAC(Role-Based Access Control,基于角色的访问控制)模型的简要指南:
1. 设计权限模型
首先,你需要设计一套权限模型,包括角色(Role)、权限(Permission)和资源(Resource)。每个角色可以关联多个权限,每个用户则对应一个或多个角色。
2. 后端接口对接
后端提供API供前端查询用户的角色和权限信息。用户登录后,后端返回包含权限信息的Token,前端将其存储(如LocalStorage或Vuex)并用于后续的权限验证。
3. 路由权限控制
-
配置路由元数据:在Vue Router的路由配置中,可以利用
Javascriptmeta
字段来标记路由需要的权限。例如:1{ 2 path: '/admin', 3 name: 'Admin', 4 component: AdminView, 5 meta: { requiresAuth: true, permissions: ['admin'] } 6}
-
全局前置守卫:使用Vue Router的全局前置守卫(如
beforeEach
),检查用户是否具有访问当前路由所需的权限。
4. 组件权限控制
对于需要权限控制的组件或按钮,可以通过以下方式处理:
-
自定义指令:创建如
Javascriptv-permission
的自定义指令,用于检查用户是否有权限显示或操作某个组件。1Vue.directive('permission', { 2 inserted(el, binding) { 3 const { value } = binding; 4 if (!hasPermission(value)) { 5 el.parentNode && el.parentNode.removeChild(el); 6 } 7 } 8});
-
计算属性/方法:在组件内部,通过计算属性或方法来决定是否渲染某些元素。
5. 动态菜单生成
根据用户权限动态生成侧边栏菜单或顶部导航。在获取到用户权限信息后,筛选出用户有权访问的菜单项并渲染。
6. Vuex集成
可以考虑使用Vuex来集中管理权限状态,使得权限信息在整个应用中易于管理和同步。
示例代码片段
Javascript
1// Vuex store 示例
2const store = new Vuex.Store({
3 state: {
4 userPermissions: [], // 用户权限集合
5 },
6 mutations: {
7 SET_PERMISSIONS(state, permissions) {
8 state.userPermissions = permissions;
9 },
10 },
11 actions: {
12 async fetchPermissions({ commit }) {
13 // 从后端获取权限并提交到mutations
14 },
15 },
16});
17
18// 路由守卫示例
19router.beforeEach((to, from, next) => {
20 if (to.meta.requiresAuth && !store.state.userPermissions.includes(to.meta.permissions)) {
21 next('/unauthorized'); // 如果没有权限,重定向到未授权页面
22 } else {
23 next();
24 }
25});
结论
Vue权限分配是一个涉及前后端协作、路由管理、状态管理以及组件层面控制的综合过程。通过上述步骤,可以实现灵活且安全的权限控制系统。
W3C 标准是什么
W3C标准是由万维网联盟(World Wide Web Consortium,简称W3C)制定的一系列规范和指南,旨在确保网络的长期增长和 interoperability(互操作性)。W3C成立于1994年,是一个国际性的中立组织,负责推荐和维护Web相关的技术和协议标准。
W3C标准主要包括三个核心方面:
-
结构(Structure): 定义了网页内容的组织方式。这方面的标准语言主要是XHTML和XML。XHTML强调严格的语法和文档结构,而XML则是一种可扩展的标记语言,用于编码数据。
-
表现(Presentation): 控制网页内容如何展示给用户。这一类别中最重要的是CSS(层叠样式表),它允许开发者分离网页的内容与表现形式,提高网页的可维护性和可访问性。
-
行为(Behavior): 确定了网页如何响应用户的交互。这一部分的关键技术包括DOM(文档对象模型),它为文档定义了一个标准的对象模型,并允许程序和脚本动态更新和构造文档内容;以及ECMAScript(通常通过其实现JavaScript),这是一种用于网页脚本编程的主要语言,支持网页上的交互性和动态功能。
遵循W3C标准的好处包括但不限于:
- 跨浏览器兼容性:使网站能在不同的浏览器和设备上一致地显示和运行。
- 易维护性:通过分离内容、表现和行为,简化网站的维护和升级。
- 无障碍性:有助于创建对残障用户更友好的网站,符合WCAG(Web Content Accessibility Guidelines)等无障碍标准。
- 搜索引擎优化:清晰的结构和语义化的标签有利于搜索引擎更好地理解网页内容,从而可能提高搜索排名。
W3C的标准涵盖了从基本的HTML和CSS到更高级的Web API、Web Services、语义Web、隐私保护、国际化、移动Web等多个方面,持续推动着Web技术的健康发展。
var、let、const之间的区别
一、var
在ES5中,顶层对象的属性和全局变量是等价的,用var
声明的变量既是全局变量,也是顶层变量
注意:顶层对象,在浏览器环境指的是window
对象,在 Node
指的是global
对象
var a = 10;
console.log(window.a) // 10
使用var
声明的变量存在变量提升的情况
console.log(a) // undefined
var a = 20
在编译阶段,编译器会将其变成以下执行
var a
console.log(a)
a = 20
使用var
,我们能够对一个变量进行多次声明,后面声明的变量会覆盖前面的变量声明
var a = 20
var a = 30
console.log(a) // 30
在函数中使用使用var
声明变量时候,该变量是局部的
var a = 20
function change(){
var a = 30
}
change()
console.log(a) // 20
而如果在函数内不使用var
,该变量是全局的
var a = 20
function change(){
a = 30
}
change()
console.log(a) // 30
二、let
let
是ES6
新增的命令,用来声明变量
用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效
{
let a = 20
}
console.log(a) // ReferenceError: a is not defined.
不存在变量提升
console.log(a) // 报错ReferenceError
let a = 2
这表示在声明它之前,变量a
是不存在的,这时如果用到它,就会抛出一个错误
只要块级作用域内存在let
命令,这个区域就不再受外部影响
var a = 123
if (true) {
a = 'abc' // ReferenceError
let a;
}
使用let
声明变量前,该变量都不可用,也就是大家常说的“暂时性死区”
最后,let
不允许在相同作用域中重复声明
let a = 20
let a = 30
// Uncaught SyntaxError: Identifier 'a' has already been declared
注意的是相同作用域,下面这种情况是不会报错的
let a = 20
{
let a = 30
}
因此,我们不能在函数内部重新声明参数
function func(arg) {
let arg;
}
func()
// Uncaught SyntaxError: Identifier 'arg' has already been declared
三、const
const
声明一个只读的常量,一旦声明,常量的值就不能改变
const a = 1
a = 3
// TypeError: Assignment to constant variable.
这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值
const a;
// SyntaxError: Missing initializer in const declaration
如果之前用var
或let
声明过变量,再用const
声明同样会报错
var a = 20
let b = 20
const a = 30
const b = 30
// 都会报错
const
实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等同于常量
对于复杂类型的数据,变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的,并不能确保改变量的结构不变
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
其它情况,const
与let
一致
四、区别
var
、let
、const
三者区别可以围绕下面五点展开:
- 变量提升
- 暂时性死区
- 块级作用域
- 重复声明
- 修改声明的变量
- 使用
变量提升
var
声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let
和const
不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
暂时性死区
var
不存在暂时性死区
let
和const
存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
块级作用域
var
不存在块级作用域
let
和const
存在块级作用域
// var
{
var a = 20
}
console.log(a) // 20
// let
{
let b = 20
}
console.log(b) // Uncaught ReferenceError: b is not defined
// const
{
const c = 20
}
console.log(c) // Uncaught ReferenceError: c is not defined
重复声明
var
允许重复声明变量
let
和const
在同一作用域不允许重复声明变量
// var
var a = 10
var a = 20 // 20
// let
let b = 10
let b = 20 // Identifier 'b' has already been declared
// const
const c = 10
const c = 20 // Identifier 'c' has already been declared
修改声明的变量
var
和let
可以
const
声明一个只读的常量。一旦声明,常量的值就不能改变
// var
var a = 10
a = 20
console.log(a) // 20
//let
let b = 10
b = 20
console.log(b) // 20
// const
const c = 10
c = 20
console.log(c) // Uncaught TypeError: Assignment to constant variable
使用
能用const
的情况尽量使用const
,其他情况下大多数使用let
,避免使用var
你能实现多少种水平垂直居中的布局(定宽高和不定宽高)
一、背景
在开发中经常遇到这个问题,即让某个元素的内容在水平和垂直方向上都居中,内容不仅限于文字,可能是图片或其他元素
居中是一个非常基础但又是非常重要的应用场景,实现居中的方法存在很多,可以将这些方法分成两个大类:
- 居中元素(子元素)的宽高已知
- 居中元素宽高未知
#二、实现方式
实现元素水平垂直居中的方式:
-
利用定位+margin:auto
-
利用定位+margin:负值
-
利用定位+transform
-
table布局
-
flex布局
-
grid布局
#利用定位+margin:auto
先上代码:
<style>
.father{
width:500px;
height:300px;
border:1px solid #0a3b98;
position: relative;
}
.son{
width:100px;
height:40px;
background: #f0a238;
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
margin:auto;
}
</style>
<div class="father">
<div class="son"></div>
</div>
父级设置为相对定位,子级绝对定位 ,并且四个定位属性的值都设置了0,那么这时候如果子级没有设置宽高,则会被拉开到和父级一样宽高
这里子元素设置了宽高,所以宽高会按照我们的设置来显示,但是实际上子级的虚拟占位已经撑满了整个父级,这时候再给它一个margin:auto
它就可以上下左右都居中了
#利用定位+margin:负值
绝大多数情况下,设置父元素为相对定位, 子元素移动自身50%实现水平垂直居中
<style>
.father {
position: relative;
width: 200px;
height: 200px;
background: skyblue;
}
.son {
position: absolute;
top: 50%;
left: 50%;
margin-left:-50px;
margin-top:-50px;
width: 100px;
height: 100px;
background: red;
}
</style>
<div class="father">
<div class="son"></div>
</div>
整个实现思路如下图所示:
- 初始位置为方块1的位置
- 当设置left、top为50%的时候,内部子元素为方块2的位置
- 设置margin为负数时,使内部子元素到方块3的位置,即中间位置
这种方案不要求父元素的高度,也就是即使父元素的高度变化了,仍然可以保持在父元素的垂直居中位置,水平方向上是一样的操作
但是该方案需要知道子元素自身的宽高,但是我们可以通过下面transform
属性进行移动
#利用定位+transform
实现代码如下:
<style>
.father {
position: relative;
width: 200px;
height: 200px;
background: skyblue;
}
.son {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 100px;
height: 100px;
background: red;
}
</style>
<div class="father">
<div class="son"></div>
</div>
translate(-50%, -50%)
将会将元素位移自己宽度和高度的-50%
这种方法其实和最上面被否定掉的margin负值用法一样,可以说是margin
负值的替代方案,并不需要知道自身元素的宽高
#table布局
设置父元素为display:table-cell
,子元素设置 display: inline-block
。利用vertical
和text-align
可以让所有的行内块级元素水平垂直居中
<style>
.father {
display: table-cell;
width: 200px;
height: 200px;
background: skyblue;
vertical-align: middle;
text-align: center;
}
.son {
display: inline-block;
width: 100px;
height: 100px;
background: red;
}
</style>
<div class="father">
<div class="son"></div>
</div>
#flex弹性布局
还是看看实现的整体代码:
<style>
.father {
display: flex;
justify-content: center;
align-items: center;
width: 200px;
height: 200px;
background: skyblue;
}
.son {
width: 100px;
height: 100px;
background: red;
}
</style>
<div class="father">
<div class="son"></div>
</div>
css3
中了flex
布局,可以非常简单实现垂直水平居中
这里可以简单看看flex
布局的关键属性作用:
-
display: flex时,表示该容器内部的元素将按照flex进行布局
-
align-items: center表示这些元素将相对于本容器水平居中
-
justify-content: center也是同样的道理垂直居中
#grid网格布局
<style>
.father {
display: grid;
align-items:center;
justify-content: center;
width: 200px;
height: 200px;
background: skyblue;
}
.son {
width: 10px;
height: 10px;
border: 1px solid red
}
</style>
<div class="father">
<div class="son"></div>
</div>
这里看到,gird
网格布局和flex
弹性布局都简单粗暴
#小结
上述方法中,不知道元素宽高大小仍能实现水平垂直居中的方法有:
- 利用定位+margin:auto
- 利用定位+transform
- flex布局
- grid布局
#三、总结
根据元素标签的性质,可以分为:
- 内联元素居中布局
- 块级元素居中布局
#内联元素居中布局
水平居中
- 行内元素可设置:text-align: center
- flex布局设置父元素:display: flex; justify-content: center
垂直居中
- 单行文本父元素确认高度:height === line-height
- 多行文本父元素确认高度:display: table-cell; vertical-align: middle
#块级元素居中布局
水平居中
- 定宽: margin: 0 auto
- 绝对定位+left:50%+margin:负自身一半
垂直居中
- position: absolute设置left、top、margin-left、margin-top(定高)
- display: table-cell
- transform: translate(x, y)
- flex(不定高,不定宽)
- grid(不定高,不定宽),兼容性相对比较差
#参考文献
- https://juejin.cn/post/6844903982960214029#heading-10
401、403、404报错
① 401和403
说明: 由于 nginx 导致的'401、403'很不常见,这里不再细讲,后续会讲解两个'独特'的案例
1) 401 Unauthorized --> 权限认证机制、Cookie、Token --> 请求没有经过'授权'
2) 403 Forbidden --> '请求被拒绝访问' <-- '有认证,认证不对或者权限不足'
404
error.log:
'No such file or directory'
强调: 404 '报错'的本质'语义'就是请求的'服务端资源'找不到导致,至少'服务端'是这样'理性'认为