Vue 前后端实战:组件通信、JWT 登录与接口权限控制

📅 2026/8/25 13:06:20
Vue 前后端实战:组件通信、JWT 登录与接口权限控制
一、组件通信组件之间默认拥有独立的数据作用域。父子组件不能直接共享data需要通过明确的通信方式传值。清晰的数据流可以让组件更容易调试也能避免一个组件随意修改另一个组件内部状态。二、父传子props父组件在子组件标签上绑定属性子组件使用props接收。Props 是单向数据流父组件可以更新传入值子组件不应该直接修改 props而应通过事件通知父组件修改源数据。!-- ParentView.vue -- template div classparent h1父组件/h1 Child :namename :is-viptrue / /div /template script import Child from /components/Child.vue export default { name: ParentView, components: { Child }, data() { return { name: 彭于晏 } } } /script!-- components/Child.vue -- template div classchild p姓名{{ name }}/p p v-ifisVip会员用户/p /div /template script export default { name: Child, props: { name: { type: String, required: true }, isVip: { type: Boolean, default: false } } } /scriptHTML 属性使用短横线命名is-vip在 JavaScript 中对应驼峰形式isVip。使用对象形式声明 props 可以增加类型、必填和默认值校验比简单数组更容易维护。三、子传父自定义事件子组件不能直接修改父组件的数据可以通过$emit派发自定义事件并把需要传递的值作为参数。父组件监听事件后更新自己的data。!-- ParentView.vue -- template div p子组件传来的内容{{ message }}/p ChildEditor submit-messagereceiveMessage / /div /template script import ChildEditor from /components/ChildEditor.vue export default { components: { ChildEditor }, data() { return { message: } }, methods: { receiveMessage(value) { this.message value } } } /script!-- ChildEditor.vue -- template div input v-model.trimtext placeholder输入内容 button clicksubmit发送给父组件/button /div /template script export default { data() { return { text: } }, methods: { submit() { this.$emit(submit-message, this.text) } } } /script事件名称需要与父组件监听的名称一致。可以把$emit理解为“子组件发出通知”而不是直接调用父组件方法这样组件之间的依赖更松散。四、ref 与 parentref可以让父组件在挂载后获取 DOM 元素或子组件实例。它适合聚焦输入框、读取 DOM 尺寸、调用子组件公开方法等命令式操作但不应取代 props 和事件这种声明式通信。template div input refsearchInput ChildPanel refchildPanel / button clickoperate执行操作/button /div /template script import ChildPanel from /components/ChildPanel.vue export default { components: { ChildPanel }, methods: { operate() { // 普通标签的 ref 是 DOM 对象。 this.$refs.searchInput.focus() // 组件的 ref 是子组件实例可以调用它公开的方法。 this.$refs.childPanel.changeTitle(父组件设置的标题) } } } /script$refs在mounted之后才可靠可用。子组件中的this.$parent可以取得父组件实例但会造成强耦合实际项目应优先使用 props、事件或状态管理工具。五、Django 跨域与 JWT 登录前端开发服务器和 Django API 常运行在不同端口浏览器会按同源策略拦截跨域请求。Django 可以使用django-cors-headers添加 CORS 响应头。pipinstalldjango-cors-headers djangorestframework-simplejwt# settings.pyINSTALLED_APPS[# ...corsheaders,rest_framework,]MIDDLEWARE[corsheaders.middleware.CorsMiddleware,# ...]# 开发环境可以临时允许前端开发服务器生产环境应列出明确来源。CORS_ALLOWED_ORIGINS[http://localhost:8080,]JWT 登录通常是前端提交用户名密码后端通过 Django Auth 校验成功后签发 access token前端保存 token后续请求在Authorization: Bearer token中携带。不要把密码保存到浏览器或直接拼到 URL 中。1. 自定义登录返回格式# app01/serializers.pyfromrest_framework_simplejwt.serializersimportTokenObtainPairSerializerclassLoginSerializer(TokenObtainPairSerializer):defvalidate(self,attrs):datasuper().validate(attrs)return{code:100,msg:登录成功,access:data[access],refresh:data[refresh],}# settings.pySIMPLE_JWT{TOKEN_OBTAIN_SERIALIZER:app01.serializers.LoginSerializer,}如果项目还需要统一处理异常应保留 HTTP 状态码信息不要把所有失败都返回 200。前端既可以读取业务code也应根据 HTTP 状态码处理未授权和服务器错误。2. 电影接口# app01/views.pyimportjsonfromrest_framework.responseimportResponsefromrest_framework.viewsetsimportViewSetfromrest_framework.decoratorsimportactionclassFilmViewSet(ViewSet):action(methods[GET],detailFalse,url_pathlist)deflist_films(self,request):withopen(films.json,encodingutf-8)asfile:datajson.load(file)returnResponse(data)# urls.pyfromdjango.urlsimportpathfromrest_framework.routersimportSimpleRouterfromrest_framework_simplejwt.viewsimportTokenObtainPairViewfromapp01.serializersimportLoginSerializerfromapp01.viewsimportFilmViewSet routerSimpleRouter()router.register(films,FilmViewSet,basenamefilms)urlpatterns[path(login/,TokenObtainPairView.as_view(serializer_classLoginSerializer)),]urlpatternsrouter.urlsRouter 会根据 ViewSet 自动生成接口路径。实际项目更推荐使用 Serializer 和 ModelViewSet 管理数据库数据而不是在视图中直接读取 JSON 文件。六、Vue 登录与带 Token 请求1. 路由配置// router/index.jsconstroutes[{path:/login,name:login,component:LoginView},{path:/films,name:films,component:FilmView}]2. 登录页面template form submit.preventlogin input v-model.trimusername placeholder用户名 input v-modelpassword typepassword placeholder密码 button :disabledloading{{ loading ? 登录中... : 登录 }}/button p v-iferror{{ error }}/p /form /template script import axios from axios export default { data() { return { username: , password: , loading: false, error: } }, methods: { async login() { this.loading true this.error try { const response await axios.post(http://127.0.0.1:8000/login/, { username: this.username, password: this.password }) if (response.data.code 100) { localStorage.setItem(access, response.data.access) localStorage.setItem(refresh, response.data.refresh) this.$router.push(/films) } else { this.error response.data.msg || 登录失败 } } catch (err) { this.error 用户名或密码错误 } finally { this.loading false } } } } /script示例使用localStorage保存 access token适合课堂演示。生产环境需要根据安全要求评估 XSS、刷新 token、退出登录和存储位置不能盲目把敏感凭证长期保存在浏览器。3. 请求电影数据template div h1最新电影/h1 p v-iferror{{ error }}/p article v-forfilm in films :keyfilm.id h3{{ film.name }}/h3 p{{ film.synopsis }}/p img :srcfilm.poster :altfilm.name width200 /article /div /template script import axios from axios export default { data() { return { films: [], error: } }, async created() { try { const token localStorage.getItem(access) const response await axios.get(http://127.0.0.1:8000/films/list/, { headers: { Authorization: Bearer ${token} } }) this.films response.data.results || response.data } catch (err) { this.error 电影数据加载失败或登录已过期 } } } /script实际项目可以创建 axios 实例和请求拦截器自动附加 token收到 401 时统一清理凭证并跳转登录页避免每个页面重复写请求头。七、scoped 样式在style上添加scoped后Vue 会为当前组件的元素生成特殊属性并把样式选择器转换为只匹配当前组件的形式减少样式互相污染。template h1 classtitle当前组件标题/h1 /template style scoped .title { color: #409eff; } /stylescoped不是安全隔离也不会自动隔离第三方组件内部样式。全局重置样式、主题变量和公共布局可以写在不带scoped的全局样式文件中。八、Element UI 集成Element UI 是面向 Vue 2 的桌面端组件库Vue 3 应使用 Element Plus。Vant 更适合移动端Ant Design Vue 适合企业级后台等场景。选择组件库时要先确认 Vue 版本和目标平台。# Vue 2 项目npminstallelement-ui--save// main.jsimportVuefromvueimportElementUIfromelement-uiimportelement-ui/lib/theme-chalk/index.cssimportAppfrom./App.vueVue.use(ElementUI)newVue({render:hh(App)}).$mount(#app)之后可以按照官方文档使用el-button、el-table、el-form等组件。按需引入可以减少打包体积大型项目还应统一主题和组件使用规范。九、props 的进阶校验简单数组写法适合快速示例正式项目建议使用对象写法声明类型、必填和默认值。validator可以增加自定义校验但它主要用于开发期提示不应替代后端校验。exportdefault{props:{name:{type:String,required:true},count:{type:Number,default:0,validator(value){returnvalue0}},enabled:{type:Boolean,default:false}}}布尔属性、数字属性和对象属性都应按真实数据类型传递。例如:enabledtrue传入布尔值而enabledtrue只是字符串。父组件传递对象时子组件也不应直接修改其内部属性必要时复制为本地数据或通过事件通知父组件。十、总结与练习父传子使用 props子传父使用$emitref和$parent属于命令式访问应谨慎使用。JWT 登录包括认证、签发 token、前端保存和后续请求携带四个步骤token 过期要统一处理。scoped适合组件局部样式Element UI 和 Element Plus 要根据 Vue 版本选择。Props 对象声明可以提供类型、必填、默认值和校验增强组件的可维护性。练习实现一个登录后访问电影列表的 Vue Django 项目并补充退出登录、401 跳转和 token 刷新逻辑。