mobx 和 redux 的区别
Redux 与 Mobx 对比
redux 中的中间件
[]
webpack 打包的过程
webpack 打包过程
webpack 打包优化方法
webpack 打包优化
webpack 当中自己写过 plugin 吗 自定义 plugin
webpack 自定义 plugin
你在 flutter 里面做了什么
我在里面充当的是视图层面的实现,比如:列表的搭建,按钮的搭建,等等。
设计一套权限相关的
首先前端新建一个配置文件,假定当前系统设定三中角色
复制 export const permission_list = {
member:["List","Detail"], //普通会员
admin:["List","Detail","Manage"], // 管理员
super_admin:["List","Detail","Manage","Admin"] // 超级管理员
}
数组里每个值对应着前端路由配置的 name 值.普通会员能访问列表页和详情页,管理员能额外访问内容管理页面,超级管理员能额外访问人员管理页面.
整个运作流程简述如下.当用户登录成功之后,通过接口返回值得知用户数据和所属角色.拿到角色值后就去配置文件里取出该角色能访问的页面列表数组,随后将这部分权限数据加载到应用中从而达到权限控制的目的.
比方说用户登录成功后,后端接口数据返回如下:
复制 {
user_id:1,
user_name:"张三",
permission_list:["List","Detail","Manage"]
}
通过接口的返回值 permission_list 可配置路由
复制 //静态路由
export const routes = [
{
path: '/login', //登录页面
name: 'Login',
component: Login,
},
{
path:"/myCenter", // 个人中心
name:"MyCenter",
component: MyCenter,
meta:{
need_login:true //需要登录
}
},
{
path:"/", // 首页
name:"Home",
component: Home,
}
]
//动态路由
export const dynamic_routes = [
{
path:"/list", // 列表页
name:"List",
component: List
},
{
path:"/detail", // 详情页
name:"Detail",
component: Detail
},
{
path:"/manage", // 内容管理页
name:"Manage",
component: Manage
},
{
path:"/admin", // 人员管理页
name:"Admin",
component: Admin
}
]
将路由分成两部分,静态路由 routes 和动态路由 dynamic_routes.静态路由里面的页面是所有角色都能访问的,它里面主要区分登录访问和非登录访问,处理的逻辑与上面介绍的登录权限控制一致.
登录完之后,将后台数据存到 localstorage 或者 vuex 里面,然后动态的添加路由信息
复制 import store from "@/store";
export const routes = [...]; //静态路由
export const dynamic_routes = [...]; //动态路由
const router = createRouter({ //创建路由对象
history: createWebHashHistory(),
routes,
});
//动态添加路由
if(store.state.user != null){ //从vuex中拿到用户信息
//用户已经登录
const { permission_list } = store.state.user; // 从用户信息中获取权限列表
const allow_routes = dynamic_routes.filter((route)=>{ //过滤允许访问的路由
return permission_list.includes(route.name);
})
allow_routes.forEach((route)=>{ // 将允许访问的路由动态添加到路由栈中
router.addRoute(route);
})
}
export default router;
这样就实现了用户只能按照他对应的权限列表里的规则访问到相应的页面,至于那些他无权访问的页面,路由实例根本没有添加相应的路由信息,因此即使用户在浏览器强行输入路径越权访问也是访问不到的.
由于 vue-router 4 废除了之前的 router.addRoutes,换成了 router.addRoute.每一次只能一个个添加路由信息,所以要将 allow_routes 遍历循环添加.
切换用户
切换用户信息是非常常见的功能,但是应用在切换成不同账号后可能会引发一些问题.例如用户先使用超级管理员登录,由于超级管理员能访问所有页面,因此所有页面路由信息都会被添加到路由实例里.
解决方案有两个:
第一是用户每次切换账户后刷新浏览器重新加载,刷新后的路由实例是重新配置的所以可以避免这个问题,但是刷新页面会带来不好的体验.
第二个方案是当用户选择登出后,清除掉路由实例里面处存放的路由栈信息(代码如下).
复制 const router = useRouter(); // 获取路由实例
const logOut = () => { //登出函数
//将整个路由栈清空
const old_routes = router.getRoutes();//获取所有路由信息
old_routes.forEach((item) => {
const name = item.name;//获取路由名词
router.removeRoute(name); //移除路由
});
//生成新的路由栈
routes.forEach((route) => {
router.addRoute(route);
});
router.push({ name: "Login" }); //跳转到登录页面
};
路由栈清空后什么页面都不能访问了,甚至登录页面都访问不了.所以需要再把静态的路由列表 routes 引入进来,使用 router.addRoute 再添加进入.这样就能让路由栈恢复到最初的状态.
内容权限控制
自定义指令
复制 import router from "./router";
import store from "./store";
const app = createApp(App); //创建vue的根实例
app.directive("permission", {
mounted(el, binding, vnode) {
const permission = binding.value; // 获取权限值
const page_name = router.currentRoute.value.name; // 获取当前路由名称
const have_permissions = store.state.permission_list[page_name] || ""; // 当前用户拥有的权限
if (!have_permissions.includes(permission)) {
el.parentElement.removeChild(el); //不拥有该权限移除dom元素
}
},
});
当元素挂载完毕后,通过 binding.value 获取该元素要求的权限编码.然后拿到当前路由名称,通过路由名称可以在 vuex 中获取到该用户在该页面所拥有的权限编码.如果该用户不具备访问该元素的权限,就把元素 dom 移除.
复制 <template>
<div>
<button v-permission="'U'">修改</button> <button v-permission="'D'">删除</button>
</div>
<p>
<button v-permission="'C'">发布需求</button>
</p>
<!--列表页-->
<div v-permission="'R'">
...
</div>
</template>
将上面模板代码和自定义指令结合理解一下就很容易明白整个内容权限控制的逻辑.首先前端开发页面时要将页面分析一遍,把每一块内容按照权限编码分类.比如修改按钮就属于 U,删除按钮属于 D.并用 v-permission 将分析结果填写上去.
当页面加载后,页面上定义的所有 v-permission 指令就会运行起来.在自定义指令内部,它会从 vuex 中取出该用户所拥有的权限编码,再与该元素所设定的编码结合起来判端是否拥有显示权限,权限不具备就移除元素.
虽然分析过程有点复杂,但是以后每个新页面想接入权限控制非常方便.只需要将新页面的各个 dom 元素添加一个 v-permission 和权限编码就完成了,剩下的工作都交给自定义指令内部去做.
物理碰撞检测--关系线的生成
矩形
复制 /* 判断是否两个矩形发生碰撞 */
private didRectCollide(sprite: RectSprite, otherSprite: RectSprite) {
let horizontal = sprite.left + sprite.width > otherSprite.left && sprite.left < otherSprite.left + otherSprite.width;
let vertical = sprite.top < otherSprite.top + otherSprite.height && sprite.top + sprite.height > otherSprite.top;
return horizontal && vertical;
}
圆形
复制 /* 判断是否两个圆发生碰撞 */
private didCircleCollide(sprite: CircleSprite, otherSprite: CircleSprite) {
return distance(sprite.x, sprite.y, otherSprite.x, otherSprite.y) < sprite.radius + otherSprite.radius;
}
小程序方面的优化
小程序分包预加载 // 分包预分化
项目部署 --- 快速部署
小程序性能优化方法
获取字符串的长度
循环遍历字符串,将每一个字符的 uincode 进行比较,字符位的 unicode>255,那么字符长度为 2,反之,则为 1。
复制 function getStrLength(str) {
let len = 0;
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
//单字节加1
if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {
len++;
} else {
len += 2;
}
}
return len;
}