1. vuex以及本地存儲localStorage如何實現(xiàn)登錄之后在刷新的時候登錄狀態(tài)仍然在。
遇到問題——當時是期待根據(jù)localStorage中的值的改變在計算屬性中隨時得到新的值。事實上。vue中的值并不會因為localStorage中值的改變而變化的,觸發(fā)不了。所以完美的辦法就是所有和localStorage相關(guān)的都在vuex中處理,這樣狀態(tài)就可以實時改變了。
當時我的問題是——我想每次進入頁面后檢測現(xiàn)在localStorage中是否有登錄的信息,有的話就從localStorage中取,沒有的話從vuex中取,這步其實就是等待登錄后拿到值,然后在我退出登錄時,我清空localStorage里面的值,此時就不能進行一些操作或者一些顯示。。。。我是在計算屬性中獲取的。
后來借鑒上文的回答,一進入頁面觸發(fā)vuex,去把localStorage中的值給vuex中的狀態(tài)值,然后在組件中只是用狀態(tài)值就行。所有l(wèi)ocalStorage的設(shè)置值和移除值均在vuex中完成。
整個處理方式就是這樣的,很重要,要記住。由于項目在后面改變了方式,所以我又改變了不再使用localStorage來儲存值了。
//一進入頁面
created(){
this.getLocalData();
},
methods:{
...mapActions({
getLocalData:'getLocalData',
}),
}
//vuex中
getLocalData({commit}){
commit(types.GET_LOCALDATA);
},
[types.GET_LOCALDATA] (state){
if(localStorage.id)
state.sfz = localStorage.sfz;
state.user = localStorage.id;
state.pwd = localStorage.pwd;
state.loginFlag = false;
}
},
//退出登錄
[types.LOGIN_OUT] (state){
localStorage.removeItem('id');
localStorage.removeItem('sfz');
localStorage.removeItem('pwd');
state.user = '';
state.sfz ='';
state.pwd ='';
state.loginFlag = true;
},
2. Computed property "digitalCert" was assigned to but it has no setter 報錯
問題分析: 一個計算屬性,如果沒有設(shè)置 setter,也就是傳入的是一個函數(shù),或者傳入的對象里沒有 set 屬性,當你嘗試直接該改變這個這個計算屬性的值,都會報這個錯誤。
在此項目中,我寫了:
mounted() {
var _this = this;
document.onkeydown=function(e) {
if(e && e.keyCode==81 && e.ctrlKey ){ //同時按下ctrl+q
_this.digitalCert = false;
}
所以更改為以下:
computed:{
digitalCert:{
get:function(){
return this.$store.state.login.digitalCert;
},
set:function(newValue){
this.$store.state.login.digitalCert = newValue;
}
}
},
3. 在引入mapState的計算屬性時,怎么寫普通的計算屬性,以及設(shè)置getter和setter的計算屬性:
有g(shù)etter和setter:
computed:{
digitalCert:{
get:function(){
return this.$store.state.login.digitalCert;
},
set:function(newValue){
this.$store.state.login.digitalCert = newValue;
}
},
...mapState({
loginBoxFlag: state => state.login.loginBoxshow,
tipsTxt: state => state.login.tipsTxt,
}),
},
普通計算屬性:
// mapState 輔助函數(shù)幫助我們生成計算屬
computed: mapState({
// 箭頭函數(shù)可使代碼更簡練
count: state => state.count,
// 傳字符串參數(shù) 'count' 等同于 'state => state.count'
countAlias: 'count',
// 為了能使用 'this'獲取局部狀態(tài),必須使用常規(guī)函數(shù)
countPlusLocalState (state) {
return state.count + this.localCount
},
// 常規(guī) computed, 沒有使用 store的狀態(tài)
localCountAlias () {
return this.localCount
}
})
4.vuex中的請求寫成promise格式的好處。
在vuex的action里面的請求要寫成promise的形式。舉例如我所做的logout。事實上登錄的很多狀態(tài)我也可以不在vuex里面維護的,沒有必要,一開始不明白這個道理:
//在header.vue中點擊按鈕觸發(fā)logout方法
logout() {
let _this = this;
this.$confirm('你確定要注銷登錄嗎?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_this.$store.dispatch('loginOut')
}).then(() =>{
_this.searchValue = '';//就可以這樣清除本組件中的值,不需要去vuex中實現(xiàn)
_this.$message({
type: 'success',
message: '你已經(jīng)成功退出登錄'
});
}).catch((e) => {
this.$message({
type: 'info',
message: '已取消注銷登錄'
});
});
}
在vuex中維護:
//actions中:
loginOut:({dispatch,commit})=> {
return new Promise((resolve, reject) => {
utils.MlTools.ajax({
type:'get',
url:'/qbeq/userLogOut',
success:function(datas){
if(datas.code){
commit(types.LOGIN_OUT)
resolve(datas)
}
},
error:function(res){
console.log(res)
reject(datas)
}
})
})
}
//在mutations中:
[types.LOGIN_OUT] (state){
state.user = '';
state.sfz ='';
state.pwd ='';
state.loginFlag = true;
},
重點就想說明在actions中resolve()之后,在vue組件的then之后即為退出成功的處理了。
5. 數(shù)組初始化在data中而非在computed中;數(shù)組的更新必須vue.set
初始化data之后,要在請求中對這個數(shù)進行賦值,那么不能在computed中初始化,比如將這個數(shù)組的每一個元素都初始化為0;
data (){
return {
trendValueIn:new Array(10).join(",").split(",").map((item,index) => {return 0;}),
trendValueOut:new Array(10).join(",").split(",").map((item,index) => {return 0;}),
}
},
computed:{
// trendValueIn(){
// return new Array(10).join(",").split(",").map((item,index) => {return 0;})
// },
// trendValueOut(){
// return new Array(10).join(",").split(",").map((item,index) => {return 0;})
// },
},
created(){
this.reqRrendValue()
},
methods:{
reqRrendValue(){
let _this = this;
this.utils.MlTools.ajax({
url: '/qbeq/keyperson',
type: 'post',
data: {},
success(data){
_this.createdtable(data.currDay);
_this.drawLine(data.totalDay);
},
error(err){
reject(err.message)
}
})
},
createdtable(data){
let _this = this;
//今日流入
let todayDataIn = data.filter((n,m) => {
if(n.flag == 1){
return n;
}
})
//今日流出
let todayDataOut = data.filter((n,m) => {
if(n.flag == 0){
return n;
}
})
//總共的
let totalIn = 0,totalOut = 0;
todayDataIn.forEach((item,index) => {
let i = _this.theader.findIndex((value,index) => {
return value == item.type;
})
totalIn += item.num;
// _this.trendValueIn[i] = item.num;
_this.$set(_this.trendValueIn,i-1,item.num)//更新數(shù)組需要使用vue.set
})
_this.$set(_this.trendValueIn,9,totalIn)
todayDataOut.forEach((item,index) => {
let i = _this.theader.findIndex((value,index) => {
return value == item.type;
})
totalOut += item.num;
// _this.trendValueOut[i] = item.num;
_this.$set(_this.trendValueOut,i-1,item.num)//更新數(shù)組需要使用vue.set
})
_this.$set(_this.trendValueOut,9,totalOut)
},
}
6. vue中定時器的處理
(1)一進入頁面觸發(fā)定時器定時發(fā)起請求
mounted(){
let _this = this;
let timeInterval = 10000;//10s
this.$nextTick(function(){
_this.timer = setInterval(function(){
// console.log(new Date())
_this.reqRrendValue()
},timeInterval)
})
}
(2) 離開此標簽頁的時候,清除定時器,返回此標簽頁時再啟動
監(jiān)測
visibilitychange事件
mounted(){
window.addEventListener("visibilitychange",()=>{
// debugger;
if(document.hidden){
console.log("我暫時離開頁面了");
clearInterval(_this.timer);
}else{
_this.timer = setInterval(function(){
// console.log(new Date())
_this.reqRrendValue()
},timeInterval)
}
})
},
(3) 在可視區(qū)域時啟動定時器,不在時清除定時請求
let _this = this;
let timeInterval = 10*1000;//10s
// 由于我的定時器在一進入頁面的時候,不在可視區(qū)域內(nèi),所以不用啟動定時器,這塊存在無作用
//this.$nextTick(function(){
// if(isElementInViewport(document.getElementsByClassName("trend")[0])){
// _this.timer = setInterval(function(){
// console.log(11111)
// console.log(new Date())
// _this.reqRrendValue()
// },timeInterval)
// }else{
// console.log("不在可視區(qū)域,清除定時請求");
// clearInterval(_this.timer);
// }
// })
window.addEventListener("visibilitychange",() => {
clearInterval(_this.timer);
if(document.hidden){
console.log("我暫時離開頁面了");
clearInterval(_this.timer);
}else{
if(isElementInViewport(document.getElementsByClassName("trend")[0])){
_this.timer = setInterval(function(){
console.log(2222222)
console.log(new Date())
_this.reqRrendValue()
},timeInterval)
}else{
console.log("不在可視區(qū)域,清除定時請求");
clearInterval(_this.timer);
}
}
})
//監(jiān)測滾動事件
window.addEventListener("scroll",() =>{
clearInterval(_this.timer);
console.log(isElementInViewport(document.getElementsByClassName("trend")[0]));
if(isElementInViewport(document.getElementsByClassName("trend")[0])){
_this.timer = setInterval(function(){
console.log(333333)
console.log(new Date())
_this.reqRrendValue()
},timeInterval)
}else{
console.log("不在可視區(qū)域,清除定時請求");
clearInterval(_this.timer);
}
})
//元素是否在可視區(qū)域內(nèi)
function isElementInViewport (el, offset = 23) {
const box = el.getBoundingClientRect(),
top = (box.top >= -25),
left = (box.left >= 0),
bottom = (box.bottom <= (window.innerHeight || document.documentElement.clientHeight) + offset),
right = (box.right <= (window.innerWidth || document.documentElement.clientWidth) + offset);
return (top && bottom&& left && right);
}
(4)定時器必須在組件生命周期的destory時清除。(必須)
- 在本組件中寫的定時器
destroyed(){
console.log("我已經(jīng)離開頁面了");
clearInterval(this.timer);
},
- 引入的js
//在mapLoad.js中:
var scrollIntervalStart,scrollInterval;
const mapLoad = {
....
}
export {mapLoad,scrollIntervalStart,scrollInterval}
//在map.vue中:
import {mapLoad,scrollIntervalStart,scrollInterval} from '@/common/mapLoad';
destroyed(){
//清除地圖上的滾動定時
console.log("我已經(jīng)離開頁面了");
clearInterval(scrollIntervalStart);
clearInterval(scrollInterval);
},
7.自定義指令
/**
* 注冊一個全局自定義指令 v-focus
*/
Vue.directive('focus', {
// 當綁定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
/**
* 注冊一個全局自定義指令 v-ctrlq,按下ctrl+q時執(zhí)行操作
*
*/
Vue.directive('ctrlq', {
bind:function(el,binding,vnode){
function documentHandler (e) {
if (e && e.keyCode==81 && e.ctrlKey){
if (binding.expression) {
binding.value(e);
}
}
}
el.__ctrlq__ = documentHandler;
document.addEventListener('keydown',documentHandler);
},
unbind:function(el,binding){
document.removeEventListener('keydown',el.__ctrlq__);
delete el.__ctrlq__;
}
})
/**
* 注冊一個全局自定義指令 v-clickoutside,在元素之外點擊時執(zhí)行函數(shù),如點擊旁邊收起下拉框
*
*/
Vue.directive('clickoutside',{
bind:function (el, binding, vnode) {
function documentHandler (e){
if (el.contains(e.target)) {
return false;
}
if (binding.expression) {
binding.value(e);
}
}
el.__vueClickOutside__ = documentHandler;
document.addEventListener('click',documentHandler);
},
unbind: function (el, binding){
document.removeEventListener('click',el.__vueClickOutside__);
delete el.__vueClickOutside__;
}
})
8.vue-router 的理解
vue本身起的項目一般為http://localhost:8080,也就是http://localhost:8080/index.html,我們的項目如果是簡單的vue單頁面應用的話,其本身就只有一個頁面——index.html。而路由頁就相當于加了幾個hash切換,通過js動態(tài)加載其他幾個頁面的,所以其其實是http://localhost:8080/index.html/#/index,http://localhost:8080/index.html/#/list。
同時在服務器上,真實的頁面也就只有index.html,所以使用vue-router的mode:history模式就有問題了(這個原理嘛。。。)官網(wǎng)上有處理辦法,后端要配合進行配置才可以。
(官網(wǎng)上)HTML5 History 模式
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當 URL 改變時,頁面不會重新加載。
如果不想要很丑的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面。const router = new VueRouter({ mode: 'history', routes: [...] })當你使用 history 模式時,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
不過這種模式要玩好,還需要后臺配置支持。因為我們的應用是個單頁客戶端應用,如果后臺沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就不好看了。
所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
圖片名字也都需要小寫。
9.vue-router的守衛(wèi)
本項目中要求在未登錄之前,不能進入別的路由頁,所以設(shè)置全局鉤子:
const router = new Router({
base:__dirname,
routes:routes
})
router.beforeEach((to,from,next) => {
if(to.meta.requireAuth){
if(store.state.login.user){
next()
}else{
next({path:'/index'})
store.commit('LOGIN_BOX',true)
}
}else{
next();
}
})
官網(wǎng)上————導航守衛(wèi)
路由單個守衛(wèi):
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
組件內(nèi)的守衛(wèi):
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調(diào)用
// 不!能!獲取組件實例 `this`
// 因為當守衛(wèi)執(zhí)行前,組件實例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該組件被復用時調(diào)用
// 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候,
// 由于會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鉤子就會在這個情況下被調(diào)用。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調(diào)用
// 可以訪問組件實例 `this`
}
}
完整的導航解析流程
- 導航被觸發(fā)。
- 在失活的組件里調(diào)用離開守衛(wèi)。
- 調(diào)用全局的 beforeEach 守衛(wèi)。
- 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
- 在路由配置里調(diào)用 beforeEnter。
- 解析異步路由組件。
- 在被激活的組件里調(diào)用 beforeRouteEnter。
- 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
- 導航被確認。
- 調(diào)用全局的 afterEach 鉤子。
- 觸發(fā) DOM 更新。
- 用創(chuàng)建好的實例調(diào)用 beforeRouteEnter
- 守衛(wèi)中傳給 next 的回調(diào)函數(shù)。
10.vue中獲取input的type
聚集時改變type:
<input type="text" ref="pwd" v-model="rgform.rgpwd" placeholder="請輸入密碼" @focus="changeType">
changeType(e){
e.target.type = 'password';
},
//或者:
this.$refs.pwd.type
11.vue中實現(xiàn)密碼可見不可見
<input type="password" v-model="rgform.rgpwd" ref="rgspwd" placeholder="請輸入密碼" v-if="viewFlag">
<input type="text" v-model="rgform.rgpwd" ref="rgspwd" placeholder="請輸入密碼" v-else><i class="el-eye" @click="changeView"></i>
changeView(){
this.viewFlag=false;
}
12.for循環(huán)上需要有key標志,標志唯一性,否則報warning。
<el-option v-for="item in protalOptions" :label="item.name" :key="item.id" :value="item.id"></el-option>
13.vue中title圖標的添加
在webpack.dev.conf.js中:
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
favicon:'./favicon.ico'
}),
同時生產(chǎn)環(huán)境中,在webpack.prod.conf.js中:
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
favicon: './favicon.ico',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
14.ie11中打開vue項目頁面一片空白
安裝 "babel-polyfill" 即可。
在入口main.js文件引入:import 'babel-polyfill'
在webpack.base.conf.js中,
module.exports = {
entry: {
app: ["babel-polyfill", "./src/main.js"]
}
},
15.為何vue不支持IE8
(https://blog.csdn.net/csdn_yudong/article/details/78332665)
因為 Vue.js 使用了 IE8 不能模擬的 ECMAScript 5 特性。
但具體是哪些特性呢?
Object.defineProperty()
該方法允許精確添加或修改對象的屬性。一般情況下,我們?yōu)閷ο筇砑訉傩允峭ㄟ^賦值來創(chuàng)建并顯示在屬性枚舉中(for…in 或 Object.keys 方法),但這種方式添加的屬性值可以被改變,也可以被刪除。而使用 Object.defineProperty() 則允許改變這些額外細節(jié)的默認設(shè)置。例如,默認情況下,使用 Object.defineProperty()增加的屬性值是不可改變的。
16.vue的js中的動態(tài)html中的img的src寫法
只要在script中的都需要用require()的格式加載圖片
var contentHtml = `<div class="popWrap">
<div class="popTop">
<div class="imgBox">
<img src=${require("@/images/portait.jpg")}>
</div>
<ul class="infoRight">
<li><label>姓 名:</label><span>${cfeature.get('person_name')}</span>
<div class="hrefs"><a title="到檔案" class="popArchives" data-card="${cfeature.get('person_card')}"></a>
</div>
</ul>
</div>
<div class="closePopup">
<span>x</span>
</div>
</div>`;
overlay.setPosition(undefined);
if(overlay.getPosition() == undefined){
overlay.setPosition(cfeature.get('center'));
map.getView().setCenter(cfeature.get('center'));
}
map.addOverlay(overlay);
$("#popup").html(contentHtml);