Vue項目總結
Vue項目基本使用
Vue項目搭建
1. 安裝node,使用node –v檢測node版本
2. 使用npm install vue安裝vue
3. 全局安裝vue-cli,npm install vue-cli –g
4. 使用vue init webpack my-project
5. cd my-project
6. npm install
7. npm run dev
進入了vue的歡迎頁面
目錄結構
├── build // 構建相關
├── config // 配置相關
├── src // 源代碼
│ ├── assets // 主題 字體等靜態資源
│ ├── styles // 全局樣式
│ ├── js // 全局js
│ ├── img // 圖片
│ ├── components // 全局公用組件
│ ├── router // 路由
│ ├── store // 全局store管理
│ ├── view // view
│ ├── App.vue // 入口頁面
│ └── main.js // 入口 加載組件 初始化等
├── static // 第三方不打包資源
│ └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── eslintrc.js // eslint 配置項
├── .gitignore // git 忽略項
├── favicon.ico // favicon圖標
├── index.html // html模板
└── package.json //package.json
引用第三方插件的方法
newwebpack.ProvidePlugin({
$: 'jquery' ,
'jQuery': 'jquery'
})
多環境配置
Vue-cli默認只提供dev和prod兩種環境,但真正開發流程可能還會多一個測試環境check,需要做相應設置
1. 需要在packjson中增加
"build:check": "node build/check.js" |
2. 需要新建check.js,webpack.check.conf.js和config中創建check.env.js
複製build.js到check.js做相應修改
增加:var config = require('../config') var config = require('../config') 修改:var config = require('../config') var webpackConfig=require('./webpack.check.conf') rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory) rm(path.join(config.check.assetsRoot, config.check.assetsSubDirectory) |
3. webpack.conf.js修改
var env = process.env.NODE_ENV === 'testing' ? require('../config/test.env') : config.check.env 將其他的build都改爲check |
4. check-versions.js修改
exports.assetsPath = function (_path) { var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.check.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) }
5. utils。Js做相同修改
6. check.env.js
module.exports = { NODE_ENV: '"production"', API_ROOT: '"http://test.internal.auto.ifeng.com/automedia/api/media"' }
7. 在index中將build複製一份修改成check
使用組件
1、mainjs引入importtuantype from './components/common/tuantype.vue'
2、註冊Vue.component('tuantype',tuantype)
3、實例化
components: {
'tuantype': tuantype
}
4、使用
組件之間的傳遞
父組件傳遞給子組件
父組件數據如何傳遞給子組件呢?可以通過props屬性來實現
父組件
<parent> <child :child-msg="msg"></child>//這裏必須要用 - 代替駝峯 </parent>
data(){ return { msg: [1,2,3] }; } |
子組件通過props來接收數據:
props: { childMsg: { type: Array, default: [0,0,0] //這樣可以指定默認的值 } } |
子組件調用方式:1.在標籤內使用{{clildMsg}}調用
2.在方法中可以使用this.childMsg調用
子組件傳遞給父組件
子組件:
<template> <div @click="up"></div> </template>
methods: { up() { this.$emit('upup','hehe'); //主動觸發upup方法,'hehe'爲向父組件傳遞的數據 } } |
父組件:
<div> <child @upup="change":msg="msg"></child> //監聽子組件觸發的upup事件,然後調用change方法 </div> methods: { change(msg) { this.msg = msg; } } |
非父組件之間的傳遞
1.創建一個bus.js
import Vue from 'vue'
export default new Vue();
|
2.在父組件中定義後這兩個組件
相應的,foo組件是通過$emit(name,val)方法將信息發送出去,name表示發送信息的名字,val表示發送的內容,導入bus.js:
<template>
<div>
<p>the count of d is{{fooCount}}</p>
<button @click="addBar" >foo</button>
</div>
</template>
<script>
import Bus from './bus'
export default{
data(){
return{
fooCount:0
}
},
methods:{
addBar(){
this.fooCount++
Bus.$emit('addBar',this.fooCount)
}
}
}
</script> |
3.然後bar組件裏通過$on(name,callback)裏獲取到foo組件發送的內容,name表示foo組件發送的信息名字,callback表示一個回調函數,來執行獲取信息後的一些操作,代碼如下,導入bus.js:
<template>
<div>
<p>the count of d is{{barCount}}</p>
</div>
</template>
<script>
import Bus from './bus'
export default{
data(){
return{
barCount:0
}
},
created(){
Bus.$on('addBar',(arg)=>{
this.barCount=arg
})
}
}
</script> |
可以實現非父子組件的通信,但是存在一個問題,
問題一:就是在路由切換的時候,並不觸發bus.$on
事件
方案一:使用sessionStorage在路由切換的時候改變緩存的值,即可解決
問題二:多次切換路由,bus.$on監聽事件在不斷增加,事件實行每切換一次就增加一次
原因:你的 bus
是全局的,在整個頁面的生命週期裏面的,然後切換路由的時候,component
的生命週期其實是控制不到你 bus
的,也就是銷燬不了這個事件,可以在component
的beforeDestory
或者是destory
事件中,也就是在組件銷燬的時候手動執行下bus.$off("change_app_charts")
,手動銷燬事件
解決方案一:監聽事件:Bus.$on('search', this.searchArticle)
需要在當前組件添加beforeDestroy() { Bus.$off('search', this.searchArticle); },
給input框添加enter鍵
<el-input type="password"v-model="loginForm.password" placeholder="密碼"@keyup.enter.native="loginSubmit"></el-input>
@keyup.enter.native="loginSubmit"
手動改變路徑方法
this.$router.push('/autoMedia/wxAccount')
表格數據下載
1、安裝兩個依賴 npm install -S file-saver xlsx npm install -D script-loader 2、導入兩個文件vendor->Blob.js,vendor->Export2Excel.js(vendor隨意起) 3、 require.ensure([], () => { const {export_json_to_excel} = require('../../vendor/Export2Excel') const tHeader = ['品牌', '團購數量', '報名人數', '成交數量', '團購數量佔比', '報名人數佔比', '成交數量佔比'] const filterVal = ['brandName', 'grouponCount', 'signCount', 'arrivedCount', 'doneCount', 'arrivedRatio', 'doneRatio'] const list = this.saletableData const data = this.formatJson(filterVal, list) export_json_to_excel(tHeader, data, '用戶信息列表') }) } formatJson: function (filterVal, jsonData) { return jsonData.map(v => filterVal.map(j => v[j])) } 4、如果webpack報解析錯誤: 在build----webpack.base.conf.js中resolve的alias加入 'vendor': path.resolve(__dirname, '../src/vendor')
從本地json獲取數據
在build->dev-server.js中添加下邊內容:
//定義路由
var apiRoute = express.Router();
apiRoute.get('/localData',function(req, res){
res.json({
errno:0,//錯誤碼
data: localData//具體數據
})
})
//註冊定義的api
app.use('/api',apiRoute);
調用方式:this.$http.get(‘/api/localData’).then(res => {})
Vue識別html標籤的辦法
<p v-html="txt.head1.p1"></p>
使用v-html識別
Vuex狀態管理
簡單使用
1. 在main.js中添加
const store = new Vuex.Store({ state: { count: 0 }, mutations: { inc: state => state.count++, dec: state => state.count-- } })
|
2. 在組件中添加
<template> <div> {{ $store.state.count}} <button @click="inc">inc</button> </div> </template> <script> export default { methods: { inc () { this.$store.commit('inc') }, dec () { this.$store.commit('dec') } } } </script>
|
就可以實現多個組件同時操作數據
項目遇到的問題
npm run dev的問題
項目部署,需要將config中build的assetsPublicPath: './',而dev中的assetsPublicPath不可設置爲'./',否則用npm run dev會cannot get的錯誤,必須將dev中的assetsPublicPath不可設置爲'/'
vue-resource與axios請求區別
2.1get請求區別,vue-resouce的get請求
this.$http.get(this.url + '/captcha', {emulateJSON: true}).then(function (res) { this.login.authcode = res.body.data.captchaImage this.sessionId = res.body.data.sessionId }) axios的 this.$http.get(this.url + '/captcha', {emulateJSON: true}).then(response => { this.login.authcode = response.data.data.captchaImage this.sessionId = response.data.data.sessionId }) |
1.2 post請求區別 axios可以直接跨域,但是axios的post的請求必須是使用qs模塊
import qs from 'qs'; this.$http.post(this.url + '/login', qs.stringify({ account: this.login.username, password: this.login.password, checkcode: this.login.captcha, sessionId: this.sessionId }), {emulateJSON: true}).then(response => {}) |
跨域問題的解決
項目中跨域問題比較常見,在vue中的跨域方式一般使用cors跨域來實現
1. 對於vue-resource的跨域問題解決方案:增加請求頭實現跨域
this.$http.post(url,{emulateJSON: true}, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}}).then(function (res) {})
|
2.對於axios不需要添加請求有就可以實現跨域
3.在本地設置代理函數
在config的index。Js中的的dev中做如下修改
proxyTable: { '/api': { target: 'http://test.internal.auto.ifeng.com/automedia/api/media', changeOrigin: true, pathRewrite: { //需要rewrite重寫的, 如果在服務器端做了處理則可以不要這段 '^/api': '' } } },
|
直接請求api就可以實現請求代理,沒有跨域問題
攔截器的適應
需求:在任何一個頁面任何一次http請求,增加對token過期的判斷,如果token已過期,需要跳轉至登錄頁面。如果要在每個頁面中的http請求操作中添加一次判斷,那麼會是一個非常大的修改工作量。那麼vue-resource是否存在一個對於任何一次請求響應捕獲的的公共回調函數呢?答案是有的!
下邊代碼添加在main.js中
Vue.http.interceptors.push((request, next) => { console.log(this)//此處this爲請求所在頁面的Vue實例 // modify request request.method = 'POST';//在請求之前可以進行一些預處理和配置 // continue to next interceptor next((response) => {//在響應之後傳給then之前對response進行修改和邏輯判斷。對於token時候已過期的判斷,就添加在此處,頁面中任何一次http請求都會先調用此處方法 response.body = '...'; return response; }); });
|
Axios攔截器:
// http request 攔截器 axios.interceptors.request.use( config => { if (store.state.token) { // 判斷是否存在token,如果存在的話,則每個http header都加上token config.headers.Authorization = `token ${store.state.token}`; } return config; }, err => { return Promise.reject(err); });
// http response 攔截器 axios.interceptors.response.use( response => { return response; }, error => { if (error.response) { switch (error.response.status) { case401: // 返回 401 清除token信息並跳轉到登錄頁面 store.commit(types.LOGOUT); router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }) } } return Promise.reject(error.response.data) // 返回接口返回的錯誤信息 }); |
左菜單刷新問題
需求:左菜單被選中後再次刷新希望目錄再次顯示到當前目錄
解決問題:element-ui的框架
<el-menu router :default-active="$route.path" >
|
使默認選中爲當前路由,不管在任何地方改變路由,左菜單都會跟着變化
後臺登錄權限問題
1. 實現理論:前端會有一份路由表,它表示了每一個路由可訪問的權限。當用戶登錄之後,通過token獲取用戶的role,動態根據用戶的role算出其相應有權限的路由,再通過router.addRoutes動態掛載路由。但這些控制都只是頁面級的,說白了前端再怎麼做權限控制都不是絕對安全的,後端的權限驗證是逃不掉的。
2. 解決辦法:權限是有後端返回配置路由,這樣其實很痛苦
3. 前端自己實現權限問題,易於管理,但是需要完成後端的思想邏輯
自己的實現方式:
1. 實現思路:控制左邊次側邊欄導航和實現動態路由
左側邊欄使用:用v-if去判斷是否有這樣的一個權限
<el-submenu index="2" v-if="this.getUserobj&&this.getUserobj.roleArea==0"> <template slot="title" class="nav-title"><img src="//p1.ifengimg.com/auto/image/2017/0625/auth.png" alt="權限管理" class="tit_icon">權限管理 </template> <el-menu-item index="/limitManage/user" style="padding-left:48px;height:40px;line-height:40px;min-width:100px;"> 用戶管理 </el-menu-item> <el-menu-item index="/limitManage/role" style="padding-left:48px;height:40px;line-height:40px;min-width:100px;"> 角色管理 </el-menu-item> </el-submenu>
2. 改變路由
3. export const constRouterMap= [
|
默認的是不需要權限的路由
3,在main.js中引入
import router from './router/index' import { adminRouterMap, constRouterMap } from '@/router'
引入後再
router.beforeEach((to, from, next) => { if (getUserobj && getUserobj.token) { if(getUserobj.roleArea==0){ } } |
4. 這樣會有一個問題:就是當角色爲管理員的時候,切換至地方站,直接輸入管理員權限網址還是能夠被打開
5. 解決方案:就是當登錄成功之後跳轉頁面後刷新頁面一次
6. this.$router.push('/index') |
成功實現後臺權限的問題