上下固定,中間滾動布局(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 安裝插件