【小慕读书】—— 后台管理系统学习:Vue进阶
前言:最近在学习Vue+Element UI+Node.js小慕读书中后台管理系统开发课程,这里对学习过程作个笔记,方便自己和大家翻阅。
一、项目内容
二、技术栈
后端技术栈 Node + Express 架构
jwt: 实现登录认证
epub: epub列树解析
multer: 文件上传
xml2js: xml文件解析
adm-zip: 自定义的电子书解析功能
mysql: 数据库
免费提供的课程API: https://www.youbaobao.xyz/admin-docs/guide/
三、Vue 进阶
1、$emit 和 $on
<div id="root"> <button @click="boost">触发事件</button> </div> <script> new Vue({ el: '#root', data() { return { message: 'hello vue' } }, created() { this.$on('my_events', this.handleEvents) //$on定义事件 }, methods: { handleEvents(e) { console.log(this.message, e) }, boost() { this.$emit('my_events', 'my params') //$emit消费事件 } } }) </script>
- 好处:将事件的定义和事件的消费分开,实现逻辑的解耦
- 可以为同一个事件绑定不同的方法
created() { this.$on('my_events', this.handleEvents) this.$on('my_events', this.handleEvents2) },
- 可以为不同的事件绑定同一个方法
created() { this.$on(['my_events', 'my_events2'], this.handleEvents) console.log(_events) }, // { // myevents: [f], // myevents2: [f] // }
- this.$emit进行了try...catch的处理,抛出异常不会中断程序的执行
2. directive 用法
<div id="root"> <div v-loading="isLoading">{{data}}</div> <button @click="update">更新</button> </div> <script> Vue.directive('loading', { //loading和v-loading名字一致 update(el, binding, vnode) { console.log(el, binding, vnode) if (binding.value) { const div = document.createElement('div') div.innerText = '加载中...' div.setAttribute('id', 'loading') div.style.position = 'absolute' div.style.left = 0 div.style.top = 0 div.style.width = '100%' div.style.height = '100%' div.style.display = 'flex' div.style.justifyContent = 'center' div.style.alignItems = 'center' div.style.color = 'white' div.style.background = 'rgba(0, 0, 0, .7)' document.body.append(div) } else { document.body.removeChild(document.getElementById('loading')) } } }) new Vue({ el: '#root', data() { return { isLoading: false, data: '' } }, methods: { update() { this.isLoading = true setTimeout(() => { this.data = '用户数据' this.isLoading = false }, 3000) } } }) </script>
3. Vue.component 用法
<div id="root"> <Test :msg="message"></Test> </div> <script> Vue.component('Test', { //定义一个组件 template: '<div>{{msg}}</div>', props: { msg: { type: String, default: 'default message' } } }) new Vue({ el: '#root', data() { return { message: "Test Component" } } }) </script>
4.Vue.extend 用法
<div id="root"> <Test :msg="message"></Test> </div> <script> const component = Vue.extend({ //生成Vue的构造函数VueComponent template: '<div>{{msg}}</div>', props: { msg: { type: String, default: 'default message' } }, name: 'Test' }) Vue.component('Test') new Vue({ el: '#root', data() { return { message: "Test Extend Component" } } }) </script>
5.Vue.extend 进阶用法
- 给Vue实例添加自定义API
<head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> #loading-wrapper { position: fixed; top: 0; left: 0; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background: rgba(0,0,0,.7); color: #fff; } </style> </head>
<body> <div id="root"> <button @click="showLoading">显示Loading</button> </div> <script> function Loading(msg) { const LoadingComponent = Vue.extend({ //创建好构造函数 template: '<div id="loading-wrapper">{{msg}}</div>', props: { msg: { type: String, default: msg } }, name: 'LoadingComponent' }) const div = document.createElement('div') div.setAttribute('id', 'loading-wrapper') document.body.append(div) //关键:使用$mount将LoadingComponent直接挂载到loading-wrapper上 new LoadingComponent({ props: { msg: { type: String, default: msg } } }).$mount('#loading-wrapper') return () => { document.body.removeChild(document.getElementById('loading-wrapper')) } } //构造函数挂载到Vue.prototype Vue.prototype.$loading = Loading new Vue({ el: '#root', methods: { showLoading() { //重点:实现this.$loading const hide = this.$loading('正在加载,请稍等...') setTimeout(() => { hide() }, 2000) } } }) </script>
6.Vue.use 用法
- 把Vue,extends高阶用法——给Vue添加自定义API——做成一个插件
<head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> #loading-wrapper { position: fixed; top: 0; left: 0; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background: rgba(0,0,0,.7); color: #fff; } </style> </head> <body> <div id="root"> <button @click="showLoading">显示Loading</button> </div> <script> const loadingPlugin = { install: function(vm) { //vm: Vue的构造函数 const LoadingComponent = vm.extend({ template: '<div id="loading-wrapper">{{msg}}</div>', props: { msg: { type: String, default: 'loading...' } } }, 'LoadingComponent') function Loading(msg) { const div = document.createElement('div') div.setAttribute('id', 'loading-wrapper') document.body.append(div) new LoadingComponent({ props: { msg: { type: String, default: msg } } }).$mount('#loading-wrapper') return () => { document.body.removeChild(document.getElementById('loading-wrapper')) } } vm.prototype.$loading = Loading } } //关键区别: 通过Vue,use()加载插件 Vue.use(loadingPlugin) new Vue({ el: '#root', methods: { showLoading() { const hide = this.$loading('正在加载,请稍等...') setTimeout(() => { hide() }, 2000) } } }) </script>
7.组件通信 provide 和 inject
- Vue2.2之后引入的,为了提供更好的跨组件通讯方案
<div id="root"> <Test></Test> </div> <script> function registerPlugin() { Vue.component('Test', { template: '<div>{{message}}<Test2 /></div>', //父组件通过provide:提供了将自身组件打包成一个属性elTest提供给子组件调用 provide() { return { elTest: this } }, // function 的用途是为了获取运行时环境,否则 this 将指向 window data() { return { message: 'message from Test' } }, methods: { change(component) { this.message = 'message from ' + component } } }) Vue.component('Test2', { template: '<Test3 />' }) Vue.component('Test3', { template: '<button @click="changeMessage">change</button>', //子组件通过inject获取elTest属性,标识引用到的父组件的实例 inject: ['elTest'], methods: { changeMessage() { //通过this.elTest直接就调用到父组件的change方法,并传入参数 this.elTest.change(this.$options._componentTag) } } }) } Vue.use(registerPlugin) new Vue({ el: '#root' }) </script>
8.过滤器 filter
<div id="root"> {{ message | lower }} </div> <script> new Vue({ el: '#root', filters: { //过滤器lower:将状态值全部改成小写 lower(value) { return value.toLowerCase() // hello vue } }, data() { return { message: 'Hello Vue' } } }) </script>
9.监听器 watch
<div id="root"> <h3>Watch 用法1:常见用法</h3> <input v-model="message"> <span>{{copyMessage}}</span> </div> <div id="root2"> <h3>Watch 用法2:绑定方法</h3> <input v-model="message"> <span>{{copyMessage}}</span> </div> <div id="root3"> <h3>Watch 用法3:deep + handler</h3> <input v-model="deepMessage.a.b"> <span>{{copyMessage}}</span> </div> <div id="root4"> <h3>Watch 用法4:immediate</h3> <input v-model="message"> <span>{{copyMessage}}</span> </div> <div id="root5"> <h3>Watch 用法5:绑定多个 handler</h3> <input v-model="message"> <span>{{copyMessage}}</span> </div> <div id="root6"> <h3>Watch 用法6:监听对象属性</h3> <input v-model="deepMessage.a.b"> <span>{{copyMessage}}</span> </div> <script> new Vue({ el: '#root', //常见用法1:监听器message后面直接跟一个方法 watch: { message(value) { this.copyMessage = value //根据message改变copyMessage的值 } }, data() { return { message: 'Hello Vue', copyMessage: '' } } }) new Vue({ el: '#root2', //常见用法2:监听器message后面不再直接跟一个方法, // 字符串绑定methods中的一个方法名 watch: { message: 'handleMessage' }, data() { return { message: 'Hello Vue', copyMessage: '' } }, methods: { handleMessage(value) { this.copyMessage = value } } }) new Vue({ el: '#root3', //深度监听1: 监听deepMessage.a.b属性,deep: true watch: { deepMessage: { handler: 'handleDeepMessage', //handler指向一个function deep: true } }, data() { return { deepMessage: { a: { b: 'Deep Message' } }, copyMessage: '' } }, methods: { handleDeepMessage(value) { this.copyMessage = value.a.b } } }) new Vue({ el: '#root4', //获得默认值:immediate: true 页面初始化后立即执行了一次handler watch: { message: { handler: 'handleMessage', immediate: true, //相当于 created: { this.copyMessage = this.message } } }, data() { return { message: 'Hello Vue', copyMessage: '' } }, methods: { handleMessage(value) { this.copyMessage = value } } }), new Vue({ el: '#root5', //绑定多个handler: 依次使用 watch: { message: [{ handler: 'handleMessage', }, 'handleMessage2', function(value) { this.copyMessage = this.copyMessage + '...' }] }, data() { return { message: 'Hello Vue', copyMessage: '' } }, methods: { handleMessage(value) { this.copyMessage = value }, handleMessage2(value) { this.copyMessage = this.copyMessage + '*' } } }) new Vue({ el: '#root6', //直接监听对象属性实现深度监听,节约性能 watch: { 'deepMessage.a.b': 'handleMessage' }, data() { return { deepMessage: { a: { b: 'Hello Vue' } }, copyMessage: '' } }, methods: { handleMessage(value) { this.copyMessage = value } } }) </script>
10.class 和 style 绑定的高级用法
<div id="root"> <div :class="['active', 'normal']">数组绑定多个class</div> <div :class="[{active: isActive}, 'normal']">数组包含对象绑定class</div> <div :class="[showWarning(), 'normal']">数组包含方法绑定class</div> <div :style="[warning, bold]">数组绑定多个style</div> <div :style="[warning, mix()]">数组包含方法绑定style</div> <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">style多重值</div> </div> <script> new Vue({ el: '#root', data() { return { isActive: true, warning: { color: 'orange' }, bold: { fontWeight: 'bold' } } }, methods: { showWarning() { return 'warning' }, mix() { return { ...this.bold, fontSize: 20 } } } }) </script>
四、Vue2.6 新特性
1.Vue.observable
- 比Vuex更方便的使用Vue的响应式属性
<div id="root"> {{message}} <button @click="change">Change</button> </div> <script> const state = Vue.observable({ message: 'Vue 2.6' }) const mutation = { setMessage(value) { state.message = value } } new Vue({ el: '#root', //注意:要在computed中计算响应式属性message的改变 computed: { message() { return state.message } }, methods: { change() { mutation.setMessage('Vue 3.0') } } }) </script>
2.插槽 slot
- v-slot只能用在组件或template上,用在div等标签上会报错
<div id="root"> <div>案例1:slot的基本用法</div> <Test> <template v-slot:header="{user}"> <div>自定义header({{user.a}})</div> </template> <template v-slot="{user}"> <div>自定义body({{user.b}})</div> </template> </Test> </div> <div id="root2"> <div>案例2:Vue2.6新特性 - 动态slot</div> <Test> <template v-slot:[section]="{section}"> <div>this is {{section}}</div> </template> </Test> <button @click="change">switch header and body</button> </div> <script> Vue.component('Test', { template: '<div>' + '<slot name="header" :user="obj" :section="\'header\'">' + '<div>默认header</div>' + '</slot>' + '<slot :user="obj" :section="\'body\'">默认body</slot>' + '</div>', data() { return { obj: { a: 1, b: 2 } } } }) new Vue({ el: '#root' }) new Vue({ el: '#root2', data() { return { section: 'header' } }, methods: { change() { this.section === 'header' ? this.section = 'default' : this.section = 'header' } } }) </script>
注:项目来自慕课网
越是迷茫、浮躁的时候,保持冷静和耐心,尤为重要