1. H5新特性
- 1.1 语义化标签
- header footer nav aside article section
- 1.2 音频、视频(audio video)
- 1.3 画布(Canvas)
- 在canvas上绘制我们需要的图形。Canvas本身是一个HTML元素,需要HTML元素的配合高度和宽度属性而定义出的一块可绘制区域,定义区域之后可被用来通过JavaScript来绘制图形及图形动画
- 1.4 localstorage 和 sessionstorage 缓存方式
2. css3新特性
-
2.1 圆角border-radius
-
2.2 阴影之box-shadow
-
2.3 阴影之text-shadow
-
2.4 背景渐变linear-gradient
-
2.5 转换transform
translate()移动 rotate()旋转 scale()缩放 skew()倾斜 3D转换之rotateX() 3D转换之rotateY() translate3d() -
2.6 过渡transition
-
2.7 动画animation
-
@keyframes创建动画
-
animation执行动画
-
2.7 绘制特殊图形
-
2.8 媒体查询
-
设置meta标签
-
媒体查询语句
-
2.9 响应式布局
-
3.0 栅格系统
-
3.1 多列
-
创建多列
-
设置每列之间的间隙
-
列边框
3. es6新特性
4. vue路由的两种模式:hash与history的区别
- hash模式url带#号,history模式不带#号
- hash的传参是基于url的,如果要传递复杂的数据,会有体积的限制,而history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中
- history模式需要后端配合将所有访问都指向index.html,否则用户刷新页面,会导致404错误
5. axios的封装
- 新建一个utils文件
- 建立request.js
import axios from 'axios'
axios.defaults.baseURL = 'http://129.211.169.131:5000'
// 设置请求拦截
axios.interceptors.request.use(config => {
// 登录之后得到token,将token放入本地,token的添加
return config
})
// 设置响应拦截
axios.interceptors.response.use(res => {
// 解构res
// 统一的错误处理
// 监听到token是否过期, 如果token过去就会返回一个状态码
return res
})
//暴露出去
export default axios
- 建立api.js
// 先引入axios从reque.js
import axios from '@/utils/request'
// 封装api
export const getGoodsList = () => axios.get('/goods/goods_list')
- 在页面使用api
// 先引入
import { getGoodsList } from '@/utils/api'
// 调用
created() {
// 获取商品列表
getGoodsList().then(res => {
console.log(res.data.data);
this.goodsList = res.data.data
// console.log(this.goodsList);
})
}
6. 面包屑的原理
7. 动态路由, vue的按钮权限如何进行控制
8. 跨域
9. 防抖节流
- 防抖
- 防抖: 就是延时操作, 多次触发只执行最后一次
- 应用场景:input框搜索,多次输入操作最终作为一次操作
<!--简单版本-->
var timer ;
function debounce(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
console.log("一秒后发请求")
},1000)
}
debounce();
- 节流
- 节流:固定时间内只执行一次
- 应用场景:btn提交,多次点击只提交一次,需要一个标识,为false时不提交
<!--简单版本-->
var flag = true;
function throttle() {
if (!flag) return false;
flag = false;
setTimeout(() => {
console.log('2秒内如论调用多少次此函数,都只会打印一次结果')
flag = true;
}, 2000)
}
throttle();
10. 请求拦截和响应拦截
11. 前置路由守卫
12. promise解决回调地狱
13. 解释双向绑定
- MVVM的双向绑定
Model <-Object.defineProperty> ViewModel <watch通知更新-> View
VM层实现双向绑定
1. 拦截model层所有的key, 产生getter/setter, 能够知道属性的变化
2. 用户操作, 触发setter, 通知VM的watch函数
3. watch函数通知view层进行视图更新
- v-model的双向绑定(v-model底层原理)
1. 使用:value绑定data动态值
2. 监听input的改变事件, 在事件中修改data
14. 页面性能优化
- keep-alive
15. data中数据更新后, 页面没有变化是什么原因
16. 404页面的配置
- router/index.js
{
path:'*',
redirect:'/404'
},
{
path:'/404',
component:()=>import('@/views/404/404.vue')
},
17. 前置路由守卫-非法路由拦截
- 前置路由守卫: 每一次 路由跳转的时候之前,触发这个守卫函数
- 路由的实例对象.beforeEach()
- 退出登录:清除所有的本地存储数据---跳转到登录
- 如果当前系统的本地存储没有登录 ---只能打开登录页
const router = new VueRouter({
routes
})
// 路由前置守卫--验证用户是否登录--如果没有登录-只能访问login
// to 前往的页面路由对象
//from 来自于 当前页面路由对象
//next 函数 next()继续下一步执行 next('/路由') 前往xx页面
router.beforeEach((to,from,next)=>{
if(to.path =='/login'){
next()
}else if(localStorage.getItem('user')){
next()
}else{
next('/login')
}
})
export default router;
18. 动态路由拆分方案-用户权限验证
- 用户权限验证 : 根据用户不同的角色 给用户不同的访问权限===能够访问的路由
- 动态路由拆分: 为了实现 权限的不同,最完美方案: 能不能把路由拆分两个路由数组
- 静态路由: 不区分角色 大家都可以访问的页面 ---只有基础页面---登录页
- 动态路由: 动态路由数组---通过 角色 生成的数组【关键--难点】
方法一: 写独立json /2个js数组 --有缺陷
- 将路由拆分为两个路由 routes 数组 只有 login * 404
- 其他的对象都放到 动态路由数组中
- src/router/routes.js
import Layout from '@/views/layout/Layout.vue'
//超级管路员的权限列表
export const superRoutes = [
{
path:'/layout',
component:Layout,
redirect:'/home',
meta:{title:"首页"},
children:[
{
path:'/home',
meta:{title:"首页"},
component:()=>import('@/views/home/Home.vue')
}
]
},
//全是二级路由
//账号管理
{
path:'/account',
component:Layout,
meta:{title:"账号管理"},
// 重定向
redirect:'/accountList',
children:[
{
path:'/accountList',
meta:{title:"账号列表"},
component:()=>import('@/views/account/AccountList.vue')
},
{
path:'/accountCenter',
meta:{title:"个人中心"},
component:()=>import('@/views/account/AccountCenter.vue')
},
{
path:'/accountEdit',
meta:{title:"修改密码"},
component:()=>import('@/views/account/AccountEdit.vue')
},
{
path:'/accountAdd',
meta:{title:"添加账号"},
component:()=>import('@/views/account/AccountAdd.vue')
}
]
},
//商品管理
{
path:'/goods',
component:Layout,
redirect:'/goodsList',
meta:{title:"商品管理"},
children:[
{
path:'/goodsList',
meta:{title:"商品列表"},
component:()=>import('@/views/goods/GoodsList.vue')
},
{
path:'/goodsAdd',
meta:{title:"商品添加"},
component:()=>import('@/views/goods/GoodsAdd.vue')
},
{
path:'/goodsType',
meta:{title:"商品分类"},
component:()=>import('@/views/goods/GoodsType.vue')
},
]
},
//订单管理
{
path:'/order',
redirect:'/orderList',
component:Layout,
meta:{title:"订单管理"},
children:[
{
path:'/orderList',
meta:{title:"订单管理" },
component:()=>import('@/views/order/OrderList.vue')
},
]
},
//店铺管理
{
path:'/layout',
redirect:'/shop',
component:Layout,
meta:{title:"店铺管理"},
children:[
{
path:'/shop',
meta:{title:"店铺管理"},
component:()=>import('@/views/shop/Shop.vue')
},
]
},
//销售同级
{
path:'/statistics',
redirect:'/goodsStatistics',
meta:{title:"销售统计"},
component:Layout,
children:[
{
path:'/goodsStatistics',
meta:{title:"商品统计"},
component:()=>import('@/views/statistics/GoodsStatistics.vue')
},
{
path:'/orderStatistics',
meta:{title:"订单统计"},
component:()=>import('@/views/statistics/OrderStatistics.vue')
},
]
}
]
//普通管理员的权限列表
export const normalRoutes = [
{
path:'/layout',
component:Layout,
redirect:'/home',
meta:{title:"首页"},
children:[
{
path:'/home',
meta:{title:"首页"},
component:()=>import('@/views/home/Home.vue')
}
]
},
//全是二级路由
//账号管理
{
path:'/account',
component:Layout,
meta:{title:"账号管理"},
// 重定向
redirect:'/accountList',
children:[
{
path:'/accountList',
meta:{title:"账号列表"},
component:()=>import('@/views/account/AccountList.vue')
},
{
path:'/accountCenter',
meta:{title:"个人中心"},
component:()=>import('@/views/account/AccountCenter.vue')
},
{
path:'/accountEdit',
meta:{title:"修改密码"},
component:()=>import('@/views/account/AccountEdit.vue')
}
]
},
//商品管理
{
path:'/goods',
component:Layout,
redirect:'/goodsList',
meta:{title:"商品管理"},
children:[
{
path:'/goodsList',
meta:{title:"商品列表"},
component:()=>import('@/views/goods/GoodsList.vue')
},
{
path:'/goodsAdd',
meta:{title:"商品添加"},
component:()=>import('@/views/goods/GoodsAdd.vue')
},
{
path:'/goodsType',
meta:{title:"商品分类"},
component:()=>import('@/views/goods/GoodsType.vue')
},
]
},
//订单管理
{
path:'/order',
redirect:'/orderList',
component:Layout,
meta:{title:"订单管理"},
children:[
{
path:'/orderList',
meta:{title:"订单管理" },
component:()=>import('@/views/order/OrderList.vue')
},
]
}
]
- 定义并暴露一个函数 做动态路由的拼接
// src/router/index.js
//在 router 实例对象后面
export const createFullRoutes=(role)=>{
if(role =='super'){
router.addRoutes([...routes,...superRoutes]);
}else{
router.addRoutes([...routes,...normalRoutes]);
}
}
- 在登录 login.vue 登录接口的成功
import { createFullRoutes} from '@/router/index.js'
// 登录的点击事件中 axios 陈功了之后 调用 该函数
let {code,id,role,token,msg}= await login(this.formData);
if(code ==0){
localStorage.setItem('user',JSON.stringify({id,role,token}));
//调用动态路由的拼接函数
createFullRoutes(role);
this.$message.success('登录成功');
//跳转
this.$router.push('/home');
}
- 测试:登录一个普通管理员账号---点击一下 店铺管理---数据统计
方法二: 递归生成
方法三: 后端返回 +管理系统配置页面
19. 全局自定义指令-权限指令
方法一: 按钮级权限
- v-if 本地存储 获取 当前用户的 role ==' super '
方法二: 自定义指令
- main.js 加上指令 v-permission
// 自定义指令
Vue.directive('permission',{
// el: 获取当前绑定这个指令的dom节点
// data: 指令传递的表达式的结果
inserted(el,data){
let {role} = JSON.parse(localStorage.getItem('user'));
//如果当前用户的role 不被包含在该按钮定义时传递的字符串中 就删除这个dom
if(!data.value.includes(role)){
el.remove();
}
}
})
- 只有 super 权限才可显示
<el-button type="primary" slot="btn" v-permission="'super'">添加分类</el-button>
- 只有 super normal 权限才可显示
<el-button type="primary" slot="btn" v-permission="'super,normal'">添加分类</el-button>
20. 拿数据后我这边渲染很慢,怎么去优化? 优化的问题?
21. 一个页面最多请求多少个接口? 一个页面请求十几个接口,是怎么解决的?
22. 路由的优化?
23. 事件冒泡?
24. 缓存?
25. 打开后白屏的原因与解决方式?
分为网页与小程序app的情况
26. 最大访问量的项目?
27. 页面转换pdf下载?
28. 加载网页,跳转的时候怎么去设置? vue动态修改网页tile?
29. vue怎么对搜索引擎进行优化? 实现单页面eso优化?
30. 事件循环
1. js异步的执行机制就是事件循环机制
-
所有的任务都在主线程执行, 形成一个执行栈
-
主线程之外存在一个任务队列(task queue),只要异步任务有了运行结果, 就会放置在任务队列中等待
-
当同步任务全部完成后就会去任务队列中读取异步任务,进入执行栈执行
-
主线程不断的重复上面三个步骤,所以叫
事件循环机制
31. vite和webpack的底层打包有什么区别?
32. webpack的五大核心,plugin和loader?
33. generator?
34. 懒加载实现的原理?
35. 前端渲染遇到js阻塞怎么办? 遇到掉帧怎么办? 掉帧的原理?
36. js的事件循环?
37. requestAnimationFrame (请求动画帧)? 用了之后怎么防止他掉帧?
38. CDN? 为什么用CDN? 前端资源都会上CDN!
39. 面对对象编程?
- 概念
- 面向对象是把事务分解为一个个对象, 然后由对象之间分工合作, 是以对象的功能来划分问题, 而不是步骤
- 优点
- 具有灵活, 代码可复用, 易维护和开发的优点
- 特性
- 封装性
- 继承性
- 多态性
40.scss的用法
安装: yarn add sass sass-loader -D
- 定义变量
- 用$去定义变量
- mixin代码重用,混入(提取公共部分)
- 用 @mixin 提取公共代码, 然后在要使用的地方用 @include 引入; 将不同的地方当作参数传进去
- 定义函数
- 用@定义一个函数,然后在函数里面编写逻辑
41.less用法
安装: yarn add less less-loader -D
- 定义变量
- 用@去定义
- mixin
- 直接 .名字(@参数){} 提取公共部分; 用 .名字()引用
42.移动端适配
设计师只会出一份设计稿(375 750)。前端会根据这个设计稿适配到各个机型。
vw: 就是屏幕宽度的百分之1,
假如我们设计稿宽度750, 设计稿上有一个板块宽度为375
请问一个问题: 实际机型 375 板块的宽度:187.5 屏幕的一半 50vw
实际机型414 板块的宽度: 207 屏幕的一半 50vw
实际的机390 板块的宽度: 195 屏幕的一半 50vw
结论:变换的实际的宽度,不变的是相对于屏幕的百分比。 将px转换为vw就能适配各个机型! 重点问题:把css中的所有px单位转换vw。借助第三方插件:postcss-px-to-viewport
- 安装插件:yarn add postcss-px-to-viewport -D
- 在项目根目录下创建一个文件:postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
// options
// 配置设计稿的宽度, 不需要你做换算,设计稿量出的宽度是多少,css中就写多少
viewportWidth: 375
}
}
}
43.vant ui框架的引入
vant官网: vant-contrib.gitee.io/vant/v2/#/z…
-
安装vue2版本的vant : yarn add vant@latest-v2
-
在mian.ts中全局引入vant的样式文件
-
按需注册组件,main.ts
import { Button } from 'vant' Vue.use(Buttton) -
去页面中使用