首页 文章 精选 留言 我的

精选列表

搜索[思考模型],共10004篇文章
优秀的个人博客,低调大师

Vue权限路由思考

前言 ❝ 年前完工了做了半年的铁路后台管理系统,系统整体业务比较复杂,这也是我到公司从 0 到 1 的 一个完整系统实践,做这个系统过程中踩了不少坑,也学到了很多。 做完这个系统没多久,紧接着又一个系统来了,没及时总结,惭愧哈!其实我们在做的后台管理系统大多数基础框架都一样,后台管理系统 主要的 是 角色权限管理 , 按钮权限管理 和 菜单管理 , 其它的业务主要围绕在这个基础之上进行扩展,最终 构成了符合业务的后台管理系统. 由于我司的项目都是采用 Vue 技术栈,那么该文章也是讲解 Vue 如何进行权限管理 进行讲解。 结尾有彩蛋哦! ❞ 权限授权登录 ❝ 任何一个后台管理系统都是 首先从登录开始,登录后返回用户基本信息,以及token。 token :存入 sessionStronge / localStronge中,然后加入到 封装好的 Axios 的 请求头中,每次请求携带token. 用户基本信息 登录成功后同时要做很多事情,具体业务具体对待。后台管理系统 登录成功后会请求当前用户的菜单权限接口,来获取用户的可访问的路由(动态路由),获取成功后,Vue Router 是不能直接使用的,必须得解析成符合Vue Router 可识别的格式 . ❞ 登录 handleLogin(){this.$refs.loginForm.validate(valid=>{if(valid){this.loading=true;login(this.loginForm).then(res=>{if(res.code===200){//存放tokensessionStorage.setItem("tokens",res.data.token);//触发Vuex来加载获取当前用户的菜单,并解析路由store.dispatch("setMenuList");this.$message({message:"登录成功",type:"success",duration:1000});this.$router.replace({path:"/dashboard"});}}).catch(()=>{this.loading=false;});}else{console.log("errorsubmit!!");returnfalse;}});} 获取当前用户菜单,解析路由 ❝ 登录成功后,本文通过 Vuex 来获取当前用户菜单和解析路由的。 store.dispatch("setMenuList"); ❞ /**@Description:*@Author:ZhangXin*@Date:2021-02-0216:10:59*@LastEditTime:2021-02-2323:03:30*@LastEditors:ZhangXin*///getMenu解析后台路由import{getMenu}from'../../utils/getMenu'//引入路由和静态路由importrouter,{constantRoutes}from'../../router/index'conststate={routerType:'',//菜单路由meunList:[]}constmutations={SET_ROUTER_TYPE(state,type){state.routerType=type},SET_ROUTER_MENULIST(state,list){//静态路由+动态路由合并完整路由constarray=constantRoutes.concat(list)state.meunList=arrayrouter.options.routes=arrayrouter.addRoutes([...array])}}constactions={setMenuList({commit,state}){//接收返回来的路由数组returnnewPromise((resolve,reject)=>{getMenu().then(res=>{commit('SET_ROUTER_TYPE','')commit('SET_ROUTER_MENULIST',res)resolve(res)})})}}exportdefault{state,mutations,actions} 解析后端返回来路由(重点) ❝ 封装好的解析后端返回来的路由,这块主要是为了在Vuex 中使用。 ❞ /**@Description:*@Author:ZhangXin*@Date:2021-02-0216:03:48*@LastEditTime:2021-02-2323:09:02*@LastEditors:ZhangXin*/importLayoutfrom'@/layout'import{getUserAuthMenu}from'@/api/user'/***@description:解析后端返回来的菜单树*@param{*}data后端返回来的路由树*@param{*}arr菜单*@return{*}*/functiontree(data,arr){data.forEach((datas,index)=>{arr.push({path:datas.path,name:datas.name,types:datas.types,hidden:datas.hidden=='true'?true:false,//当时这块踩坑了component:datas.component==='Layout'?Layout:resolve=>require([`@/views/${datas.component}.vue`],resolve),meta:{title:datas.meta.title,icon:datas.meta.icon,//用来存放按钮权限button:datas.meta.button},//redirect:datas.redirect,id:datas.id,//子路由children:[]})if(datas.children){constchildArr=tree(datas.children,[])arr[index].children=childArr}})returnarr}/***@description:获取当前登录用户的菜单*@param{*}*@return{*}*/exportfunctiongetMenu(){returnnewPromise(function(resolve,reject){getUserAuthMenu().then(res=>{if(res.code===200){constdatas=res.data//调用tree来解析后端返回来的树resolve(tree(datas,[]))}})})} 后端接收路由格式 前端接收到的真实菜单树 页面刷新,路由丢失 ❝ 到此为止,已经实现了Vue 动态权限控制 ,别高兴的太早,哈哈,一刷新页面,页面就进入了 404 页面 。 这是为什么呢 ? ❝ 因为存入Vuex 中的数据,一刷新页面,就会清空,那么当然找不到当前路由,就进入 404 页面了 . ❞ 如何处理呢? ❝ **一、 可以 将 静态和 动态 构成的完整路由 存放在sessionStronge / localStronge 中,然后页面刷新时,通过在 全局入口文件 App.vue 的 生命周期 created 中 ,将 router = sessionStronge / localStronge 存入的完整的路由,页面在刷新时,它会重新加载完整的路由。** 「二、如果是使用Vuex来获取和解析用户菜单的话, 那么你可以在全局入口文件 App.vue 的 生命周期 created 中 ,再次执行 Vuex Action 来重新加载用户菜单」 ❞ 我这块直接在 App.vue 的 生命周期 created 中 , 再次执行了 Vuex 来进行加载和解析,没有做其它操作。当然了,具体业务具体对待。 ❞ <template><divid="app"><router-viewv-if="isRouterAlive"/></div></template><script>importstorefrom"@/store";exportdefault{name:"App",provide(){return{reload:this.reload};},data(){return{isRouterAlive:true};},methods:{reload(){this.isRouterAlive=false;this.$nextTick(()=>(this.isRouterAlive=true));}},created(){//只要刷新页面,就会重新加载路由树,保证了路由不会丢失数据store.dispatch("setMenuList");}};</script> 总结 ❝ 核心思想 「1.定义符合 当前项目业务路由格式,前后端按这个接收传递」 「2.前端解析后端返回的动态路由,生成Vue Router 可识别格式,最后拼接完整路由」 「3.刷新路由丢失处理」 按钮权限控制 「1.当前组件 路由 携带可使用的 按钮权限,存入数组中,通过v-if 来判断是否显示」 「2.登录时,单独获取整个系统的按钮权限,将获取到的所有按钮 存入一个数组中,放入全局中,然后,通过 v-if 来判断是否显示」 **3. ............ ** ❞ 彩蛋思维导图 2020前端技术面试必备Vue:(一)基础快速学习篇 2020前端技术面试必备Vue:(二)Router篇 2020前端技术面试必备Vue:(二)组件篇 2020前端技术面试必备Vue:(四)Vuex状态管理 以上是Vue全家桶系列 全栈进阶:Nginx基本功能及其原理 TypeScript&nbsp;快速入门(基础篇)MYSQL常用操作指令 更多精彩文章在公众号 本文分享自微信公众号 - 前端自学社区(gh_ce69e7dba7b5)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

重新思考“云爆发”

云计算世界中无所不有。云爆发能否使新包装卷土重来? 云爆发将面临一些现实情况: 将私有云系统的当前状态与大型超大规模厂商的特性和功能相比,私有云已不再是一回事。 企业需要在私有云和公共云上维护工作负载,才能使混合云爆发正常工作;本质上,使用两个不同的平台。 很明显,爆发式的混合云概念只会增加技术堆栈的复杂性和成本,从而使那些希望用最少的资源做更多的事情的企业广泛采用。 人们可能错过了几年前科技媒体的热情,云爆发是只有当本地云的容量用尽时才利用公共云的概念。企业以某种方式认为他们可以调用应用程序的基于公共云的部分,并且可以访问相同的数据而不会造成延迟。大多数情况下,它不起作用。 现在还有其他一些要说的关于2020年和2021年云爆裂的概念。 首先,当今存在一些类似于公共云的本地解决方案,因为它们是由公共云提供商出售的。包括谷歌、Microsoft和AWS在内的大型超大规模解决方案拥有传统数据中心中存在的硬件和软件解决方案。简而言之,这些是打包为设备的公共云解决方案的缩小版本。 考虑到本地平台和公共云平台都是专门为共同工作和良好发挥而设计的,因此使用这些解决方案可以实现云爆发。目标是通过使用这些本地解决方案作为中间步骤,最终将本地工作负载移动到公共云。 其次,边缘计算已经成为现实。连接到公共云的物联网设备的使用一直存在,但是作为设备和合法服务器的基于边缘的系统的正式使用是云计算架构时代精神的一部分。 这意味着边缘计算在公共云提供程序之外具有处理和数据存储功能,这些功能也专门用于与特定的公共云一起使用。而且,公共云提供商现在直接支持边缘。那些利用边缘计算基础设施的部署系统不必从头开始构建事物,而将公共云用于后端处理。 尽管有越来越多的架构模式看起来像是云爆发,但是这个概念实际上是关于分发处理和存储的,这并不是什么新鲜事物。在这里指出发生变化的目的实际上是指出事情确实发生了变化。这就是人们喜欢采用云计算开展业务的原因。 【责任编辑: 赵宁宁 TEL:(010)68476606】

优秀的个人博客,低调大师

BroadcastReceiver的思考(1)

作为四大组件,Broadcast Receiver 使用起来比较简单。有两种注册方式:1. 在AndroidManifest中注册。2. 在代码中直接注册,这种注册需要注意的一点是:当注册此Receiver的Activity退出的时候,一定要调用unregisterReceiver 方法,这个有点像Service的Bind。 第二种方式的示例代码如下: ?[Copy to clipboard]Downloadzuiniuwang.java *BroadCasterActivity.java *com.androidtest.broadcaster * *Function:TODO * *verdateauthor *────────────────────────────────── *2011-6-8Leon * *Copyright(c)2011,TNTAllRightsReserved. */ packagecom.androidtest.broadcaster; importcom.androidtest.R; importandroid.app.Activity; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.content.IntentFilter; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.View; importandroid.widget.Button; /** *ClassName:BroadCasterActivity *Function:TODOADDFUNCTION *Reason:TODOADDREASON * *@authorLeon *@version *@sinceVer1.1 *@Date2011-6-8 */ publicclassBroadCasterActivityextendsActivity{ privateBroadcastermyBroadcaster; @Override protectedvoidonCreate(BundlesavedInstanceState){ //TODOAuto-generatedmethodstub super.onCreate(savedInstanceState); this.setContentView(R.layout.activity_broadcaster); myBroadcaster=newBroadcaster(); IntentFilterfilter=newIntentFilter(); filter.addAction("test"); this.registerReceiver(myBroadcaster,filter); Buttonbutton=(Button)this.findViewById(R.id.button1); button.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub Intentintent=newIntent("test"); BroadCasterActivity.this.sendBroadcast(intent); } }); } @Override publicvoidfinish(){ //TODOAuto-generatedmethodstub super.finish(); this.unregisterReceiver(myBroadcaster); } } classBroadcasterextendsBroadcastReceiver{ privatestaticfinalStringTAG="Broadcaster"; @Override publicvoidonReceive(Contextcontext,Intentintent){ //TODOAuto-generatedmethodstub Stringstring=intent.getAction(); Log.v(TAG,"Theactionis"+string); } } 当然我们也可以使用第一种方式,在AndroidManifest.xml定义,这样就定义了一个全局的receiver()和Activity <receiverandroid:name=".broadcaster.Broadcaster"> <intent-filter> <actionandroid:name="test"/> </intent-filter> </receiver> 本文转自 最牛傻蛋 51CTO博客,原文链接:http://blog.51cto.com/zuiniuwang/718307,如需转载请自行联系原作者

优秀的个人博客,低调大师

BroadcastReceiver的思考(3)

傻蛋提到了BroadcastReceiver是Android中最轻薄、最短小的组件,它的对象生命周期十分短暂,经过傻蛋测试在BroadcastReceiver中让线程睡眠10秒(Activity是5秒钟)的话,Android就会弹出错误(和Activity超时的错误相同),同时需要注意的是Activity、Service和BroadcastReceiver都是运行在本进程的主线程里面的。通过这个测试让傻蛋进一步产生了疑问,如果在Service中处理一个长时间的任务会怎么样? 启动一个Service,然后在Service的onCreate()方法中添加如下代码: try{ Log.v(TAG,"sleepstart….."); Thread.sleep(20000); Log.v(TAG,"sleepend….."); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } 单就是让Service睡眠20秒钟,我们会发现,sleep start…. 和 sleep end….这两个日志打印出来了,但是后台还会出现如下错误,前台弹出no response超时对话框。 当傻蛋在onCreate()中新启动一个线程来,睡眠时,程序正常。 所以总结一下:无论是 Activity、BroadcastReceiver还是Service,只要是有长时间处理的任务,就需要重新开一个线程来处理,为什么会这样?因为他们都是运行在主线程中的。 在使用BroadcastReceiver时还有一个我们需要注意的:在BroadcastReceiver的onReceive(Context context , Intent intent )这第一个context到底是哪一个context?是Activity还是Application?通过傻蛋测试发现: 如果你的BroadcastReceiver是通过在Activity中的this.registerReceiver(myBroadcaster, filter); 来注册的话,那么这个context就是这个Activity,而如果是通过AndroidManifest来注册的话,那么这个context就是:android.app.ReceiverRestrictedContext。 本文转自 最牛傻蛋 51CTO博客,原文链接:http://blog.51cto.com/zuiniuwang/720191,如需转载请自行联系原作者

优秀的个人博客,低调大师

BroadcastReceiver的思考(2)

在使用Broadcast 时我们应该注意到,BroadcastReceiver的子类别都是无状态的类别,每次收到发送广播事件后,BroadcastReceiver都会创建一个新的对象,然后再执行onReceive()函数,当onReceive()函数执行完毕后,就立刻删掉该对象,下一次再收到此广播后,又会创建一个新的对象。所以说Broadcast组建是Android中最轻薄、最短小的组建。我们把上一篇文章的代码稍作一下修改,增加了一个static的变量numStatic ,和num变量 。代码如下: ?[Copy to clipboard] DownloadBroadcaster.java /** *Broadcaster.java *com.androidtest.broadcaster * *Function:TODO * *verdateauthor *────────────────────────────────── *2011-6-9Leon * *Copyright(c)2011,TNTAllRightsReserved. */ packagecom.androidtest.broadcaster; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.util.Log; /** *ClassName:Broadcaster *Function:TODOADDFUNCTION *Reason:TODOADDREASON * *@authorLeon *@version *@sinceVer1.1 *@Date2011-6-9 */ publicclassBroadcasterextendsBroadcastReceiver{ privatestaticfinalStringTAG="Broadcaster"; privatestaticintnumStatic=100; privateintnum=100; @Override publicvoidonReceive(Contextcontext,Intentintent){ //TODOAuto-generatedmethodstub Stringstring=intent.getAction(); numStaticnumStatic=numStatic+50; num=100+50; Log.v(TAG,"Theactionis"+string+"StaticNumberis:"+numStatic +"Objectnumis:"+num); } } 多次发送广播,然后输出的结果如下,我们可以看到static Number 每次执行都会增加,而Object Num因为每次都要创建所以一直都是一个固定的值。 本文转自 最牛傻蛋 51CTO博客,原文链接:http://blog.51cto.com/zuiniuwang/718288,如需转载请自行联系原作者

优秀的个人博客,低调大师

BroadcastReceiver的思考(6)

上篇文章中我们谈到了,Service的远程沟通,既当Activity和Service不在一个进程中,它们之间是怎么相互通信的,不过只是停留在原理层面,今天傻蛋写了一个测试程序来进一步说明远程沟通机制。 Android框架的IPC沟通其实是依赖单一的IBinder接口,当Activity端呼叫IBinder接口的transact()函数时,就会透过IPC机制来呼叫远端的onTransact()函数。当调用transact函数之后,Android框架会根据线程的同步机制,等待远端的onTransact()函数执行完毕 并且返回,才继续向下面继续执行。傻蛋画了副图来进一步说明这个过程。 1. JavaBBinder是Android系统底层提供的类,其实它是个代理(代理模式),同时实现了IBinder接口,这样它onTransact()函数就能够调用myBinder的onTransact()函数 2. 当RemoteServiceTransactActivity需要夸进程来执行JavaBBinder的对象时,Android框架从RemoteServiceTransact所在的进程中启动一个线程Thread X来配合Thread A的执行,这样就变成进程内的通信了。 3. 通过这种远程代理的方式,使用者就会感觉到好像是在本地线程中执行程序一样了。 运行结果如下: 从结果我们可以看出Activity运行的线程名是main,Service运行的线程名也是main,不过和前面Activity的那个main不是一个,为了证明这一点我把它改名为main-changed,Binder所运行的线程名为Binder Thread #2 ,用来配合Activity主线程的远程调用。 测试代码如下: ?[Copy to clipboard] DownloadRemoteMusicService.java /** *RemoteMusicService.java *com.androidtest.service.mediaplayer * *Function:TODO * *verdateauthor *────────────────────────────────── *2011-5-19Leon * *Copyright(c)2011,TNTAllRightsReserved. */ packagecom.androidtest.service; importcom.androidtest.R; importcom.androidtest.parcelable.ParcelableObject; importandroid.app.Service; importandroid.content.Intent; importandroid.media.MediaPlayer; importandroid.os.Binder; importandroid.os.IBinder; importandroid.os.Parcel; importandroid.os.Parcelable; importandroid.os.RemoteException; importandroid.util.Log; /** *ClassName:RemoteMusicService *Function:TODOADDFUNCTION *Reason:TODOADDREASON * *@authorLeon *@version *@sinceVer1.1 *@Date2011-5-19 */ publicclassRemoteServiceTransactextendsService{ privateIBindermBinder=null; privateStringreplyString; @Override publicvoidonCreate(){ //TODOAuto-generatedmethodstub super.onCreate(); mBinder=newmyBinder(); Thread.currentThread().setName("ServiceThreadName:"+ Thread.currentThread().getName()+"-chenaged"); } @Override publicIBinderonBind(Intentintent){ //TODOAuto-generatedmethodstub replyString=Thread.currentThread().getName(); returnmBinder; } publicclassmyBinderextendsBinder{ @Override protectedbooleanonTransact(intcode,Parceldata,Parcelreply, intflags)throwsRemoteException{ //TODOAuto-generatedmethodstub reply.writeString(replyString+"BinderThreadis:"+Thread.currentThread().getName()); returntrue; } } } ?[Copy to clipboard] DownloadRemoteServiceTransactActivity.java /** *RemoteServiceTransactActivity.java *com.androidtest.service * *Function:TODO * *verdateauthor *────────────────────────────────── *2011-6-21Leon * *Copyright(c)2011,TNTAllRightsReserved. */ packagecom.androidtest.service; importcom.androidtest.service.mediaplayer.IMusicService; importandroid.app.Activity; importandroid.content.ComponentName; importandroid.content.Context; importandroid.content.Intent; importandroid.content.ServiceConnection; importandroid.os.Bundle; importandroid.os.IBinder; importandroid.os.Parcel; importandroid.util.Log; importandroid.view.View; importandroid.widget.Button; importandroid.widget.LinearLayout; importandroid.widget.TextView; /** *ClassName:RemoteServiceTransactActivity *Function:TODOADDFUNCTION *Reason:TODOADDREASON * *@authorLeon *@version *@sinceVer1.1 *@Date2011-6-21 */ publicclassRemoteServiceTransactActivityextendsActivity{ privatestaticfinalStringTAG=RemoteServiceTransactActivity.class.getSimpleName(); privatefinalintWC=LinearLayout.LayoutParams.WRAP_CONTENT; privatefinalintWP=LinearLayout.LayoutParams.FILL_PARENT; privateButtonbuttonRunService; privateTextViewtextView; privateIBinderiBinder; @Override protectedvoidonCreate(BundlesavedInstanceState){ //TODOAuto-generatedmethodstub super.onCreate(savedInstanceState); LinearLayoutlayout=newLinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); this.setTitle("TestRunService.."); //定义Button buttonRunService=newButton(this); buttonRunService.setId(1); buttonRunService.setText("RunService"); buttonRunService.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub ParcelsendParcel=Parcel.obtain(); ParcelreplyParcel=Parcel.obtain(); try{ iBinder.transact(2,sendParcel,replyParcel,0); //先打印出Activity的主线程名然后Service的主线程名更名然后返回,说明Service运行在不同 //的进程中 textView.setText("ActivityThreadNameis:"+Thread.currentThread().getName() +"and"+replyParcel.readString()); }catch(Exceptione){ e.printStackTrace(); } } }); //定义TextView textView=newTextView(this); textView.setText("Ready...."); //加入到Layout中 layout.addView(buttonRunService); layout.addView(textView); this.setContentView(layout); this.bindService(newIntent("com.androidtest.service.RemoteServiceTransact") ,myServiceConnection,Context.BIND_AUTO_CREATE); } privateServiceConnectionmyServiceConnection=newServiceConnection(){ @Override publicvoidonServiceConnected(ComponentNamename,IBinderbinder){ iBinder=binder; Log.d(TAG,"onServiceConnected"); } @Override publicvoidonServiceDisconnected(ComponentNamename){ Log.d(TAG,"onServiceDisconnected"); } }; } 同时在Android Manifest中需要这样来定义这个Service,以便它能够在不同的进程中运行。 <serviceandroid:enabled="true"android:process=":remote" android:name=".service.RemoteServiceTransact"> <intent-filter> <actionandroid:name="com.androidtest.service.RemoteServiceTransact"/> </intent-filter> </service> 本文转自 最牛傻蛋 51CTO博客,原文链接:http://blog.51cto.com/zuiniuwang/718292,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册