VUE实践经典记录(持续更新)
上下固定,中间滚动布局(FLEX)
<div id="app"> <div class="header"></div> <div class="views"></div> <div class="footer"></div> </div> <style> #app{display: flex;flex-direction: column;height: 100%;} .views{flex: 1; overflow-y: scroll;-webkit-overflow-scrolling: touch;} /*-webkit-overflow-scrolling: touch; 解决苹果手机下网页滑动不顺畅问题*/ .header{} /*高度随意设置*/ .footer{} /*高度随意设置*/ </style>
Vue插件封装(loading实例)
src/omponents/loading/Loading.vue <template> <div class="loading" v-show="show"> <i class="i-loading"></i> </div> </template> <script> export default { props: { show: Boolean } } </script> <style lang="scss" scoped> .loading{ width: 200px; height: 200px; position: fixed; left: 0; right: 0; bottom: 0; top: 0; margin: auto; border-radius: 6px; background: rgba(0,0,0,0.6); display: flex; justify-content: center; align-items: center; z-index: 999; } .i-loading { width: 90px; height: 90px; display: inline-block; vertical-align: middle; -webkit-animation: loading 1s steps(12, end) infinite; animation: loading 1s steps(12, end) infinite; background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat; background-size: 100%; } @keyframes loading { 0% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 360deg); transform: rotate3d(0, 0, 1, 360deg); } } </style> src/omponents/loading/index.js <script> import LoadingComponent from './loading' let $vm export default { install (Vue, options) { if (!$vm) { const LoadingPlugin = Vue.extend(LoadingComponent) $vm = new LoadingPlugin({ el: document.createElement('div') }) console.log($vm) } $vm.show = false let loading = { show (text) { $vm.show = true $vm.text = text document.body.appendChild($vm.$el) }, hide () { $vm.show = false } } if (!Vue.$loading) { Vue.$loading = loading } Vue.mixin({ created () { this.$loading = Vue.$loading } }) } } //使用 import Loading from '@/components/loading/index.js' //loading 插件 Vue.use(Loading) //使用loading插件 Vue.$loading.show() //显示 Vue.$loading.hide() //隐藏 </script>
axios全局路由拦截及结合promise对axios请求进行处理
src/utils/request.js import axios from 'axios' import {Notification, MessageBox} from 'element-ui' import store from '@/store' import {getToken} from '@/utils/auth' axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' // 创建axios实例 const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: process.env.VUE_APP_BASE_API, // 超时 timeout: 10000 }) // request拦截器 service.interceptors.request.use( config => { if (getToken()) { config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } return config }, error => { Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use(res => { const code = res.data.code if (code === 401) { MessageBox.confirm( '登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' } ).then(() => { store.dispatch('LogOut').then(() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } else if (code !== 200) { Notification.error({ title: res.data.msg }) return Promise.reject('error') } else { return res.data } }, error => { Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default service src/api.js import request from '@/utils/request' // AXIOS GET请求 // get export function listConfig(query) { return request({ url: '/system/config/list', method: 'get', params: query }) } // post export function addConfig(data) { return request({ url: '/system/config', method: 'post', data: data }) }
路由的其他一些配置
router.beforeEach((to, from, next) => { // 动态更改页面title if (to.meta.title) { document.title = to.meta.title } // 验证是否需要登陆 if (to.meta.requireAuth && JSON.stringify(store.state.user) === '{}') { next({ name: 'signIn' }) } // 如果登录状态进入登录页面则,返回到个人中心页面 if (JSON.stringify(store.state.user) !== '{}') { if (to.name === 'signIn' || to.name === 'resetPassword' || to.name === 'mobileLogin') { next({ name: 'personal', query: { type: 'records' } }) } } next() })
解决移动端click300ms问题
安装 npm install fastclick --save 使用 import fastclick from 'fastclick' fastclick.attach(document.body)
Vue父子组件通讯
父向子传递参数 Parent.vue(父组件) <template> <div> <Child :name="name"></Child> </div> </template> <script> import Child from './Child' export default{ components:{ Child }, data(){ return{ name:'hello' } } } </script> Child.vue(子组件) <template> <div> <!-- 这里的name接收了父组件传过来的参数,这里会变成hello --> {{name}} </div> </template> <script> export default{ props:{ name:String } } </script> 子向父传递参数 Child.vue(子组件) <template> <div> <button @click="toParentMsg()">我要向父节点传递参数</button> </div> </template> <script> export default{ method:{ toParentMsg(){ this.$emit('listenToChildEvent','我是要向父组件传送的数据') //listenToChildEvent 自定义事件,后面需要再父组件接收这个自定义事件 } } } </script> Parent.vue(父组件) <template> <div> <Child @listenToChildEvent = "receiveChildMsg"></Child> </div> </template> <script> import Child from './Child' export default{ components:{ Child }, data(){ }, method:{ receiveChildMsg(val){ console.log(val) //我是要向父组件传送的数据 } } } </script>
对函数进行封装全局使用
src/utils/global.js //公共方法写在这 exports.install = function (Vue, options) { /** * 对小数位进行格式化 * @data 数据 * @num 格式位数 * */ Vue.filter('decimalPlaceFormat', function (data, num) { if (!(data && num)) return '' return Number(data).toFixed(num) }); } src/main.js //引用安装 import global from './utils/global' Vue.use(global)
对指令封装全局使用
src/directive/hasPermiss.js /** * 按钮权限控制 * */ import store from './../store' export default { inserted(el,binding) { const {value} = binding; const btnPermiss = store.getters.btnPermiss; let status = btnPermiss.some(item => item == value); if(!status){ el.parentNode && el.parentNode.removeChild(el); } } } src/directive/index.js import hasPermiss from './hasPermiss' const install = function(Vue) { Vue.directive('hasPermiss',hasPermiss) } if (window.Vue) { window['hasPermiss'] = hasPermiss; Vue.use(install); } export default install src/main.js import install from './directive' Vue.use(global);
VUEX模块使用
Vuex数据操作及数据持久化
使用vuex-persistedstate插件 const store = new Vuex.Store({ state: { // 用户信息 user: {}, // 分类页数据筛选 videoListFilterTerm: { catId: 0, courseId: 0, software: 0, diff: 0, sort: 3, page: 1 } }, //获取state数据 getters: { getUser (state) { return state.user } }, //操作state数据 mutations: { setUser (state, user) { state.user = user }, setVideoListFilterTerm (state, videoListFilterTerm) { state.videoListFilterTerm = videoListFilterTerm } }, //触发mutations函数 actions: { setUser ({ commit }, user) { commit('setUser', user) }, setVideoListFilterTerm ({ commit }, videoListFilterTerm) { commit('setVideoListFilterTerm', videoListFilterTerm) } }, //插件配置 plugins: [createPersistedState({ storage: window.localStorage, reducer (val) { return { user: val.user } } })] })
Vue m3u8视频播放配置
播放m3u8视频需要用到 videojs-contrib-hls插件 安装 npm install videojs-contrib-hls --save 导入import 'videojs-contrib-hls'
Vue-router 实现模块化加载
使用该方式导入组件,打包模块会自动把组件进行模块化打包 const xxx = () => import('@/views/xxx')
Vue路由切换增加动画效果
90sheji-video/src/App.vue <template> <div id="app"> <transition name="fade" mode="out-in"> <router-view /> </transition> </div> </template> <script> export default { name: 'App' } </script> src/components/layout/Layout.vue <template> <div class="layout flex"> <Header/> <transition name="fade-transform" mode="out-in"> <keep-alive> <router-view :key="key"/> </keep-alive> </transition> <Footer/> </div> </template> <script> import Footer from '@/components/common/Footer' import Header from '@/components/common/Header' export default { data () { return { catId: '' } }, components: { Footer, Header }, computed: { key () { return this.$route.name ? this.$route.name : this.$route.fullPath } } } </script> <style> /*********************动画·start**************************/ /* fade */ .fade-enter-active, .fade-leave-active { transition: opacity 0.2s; } .fade-enter, .fade-leave-active { opacity: 0; } /* fade-transform */ .fade-transform-leave-active, .fade-transform-enter-active { transition: all 0.2s; } .fade-transform-enter { opacity: 0; transform: translateX(-20px); } .fade-transform-leave-to { opacity: 0; transform: translateX(20px); } /* breadcrumb transition */ .breadcrumb-enter-active, .breadcrumb-leave-active { transition: all 0.2s; } .breadcrumb-enter, .breadcrumb-leave-active { opacity: 0; transform: translateX(20px); } .breadcrumb-move { transition: all 0.2s; } .breadcrumb-leave-active { position: absolute; } /*********************动画·end**************************/ </style> 组件切换有两种情况,一种是兄弟与兄弟组件切换,一种是子组件与父组件之间切换,所以这里转场动画用的不一样,所以贴出了两个组件用法 <keep-alive> 增加这个标签表示组件可以被缓存起来,强烈加上 <router-view :key="key"/> 前面用了组件缓存,所以路由视图的key一定要加上,不然路由和页面有些会不匹配
vue使用官方脚手架打包上线配置
/config/index.js build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'),//入口文件 // Paths assetsRoot: path.resolve(__dirname, '../dist'),//编译后所有需要部署的文件都放到了这里 assetsSubDirectory: 'public',//静态资源存放的文件目录 assetsPublicPath: './',// ./是相对路径,/是绝对路径,这里改为相对路径,不然打包后上线图片访问不了 productionSourceMap: true,//是否开启SourceMap压缩 devtool: '#source-map', productionGzip: false,//是否开启Gzip压缩 productionGzipExtensions: ['js', 'css'],//Gzip压缩 bundleAnalyzerReport: process.env.npm_config_report }
后端解决跨域配置
resp.setHeader("Access-Control-Allow-Origin", "*"); resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
package.json文件中dependencies与devDependencies的区别
dependencies:打包上线需要用到的插件 使用npm inatall --save xxx 安装插件 devDependencies:开发环境需要用到的插件 使用npm install --save-dev xxx 安装插件
愿你走出半生,归来仍是少年