Vue項目經驗
setInterval路由跳轉繼續運行並沒有及時進行銷毀
比如一些彈幕,走馬燈文字,這類需要定時調用的,路由跳轉之后,因為組件已經銷毀了,但是setInterval還沒有銷毀,還在繼續后台調用,控制台會不斷報錯,如果運算量大的話,無法及時清除,會導致嚴重的頁面卡頓。
解決方案:在組件生命周期beforeDestroy停止setInterval
beforeDestory() {
clearInterval(this.timer);
MessageBox.close()
}
使用vue過程中你遇到了什么困難?
多級嵌套傳參,解決思路自行百度
路由嵌套
路由嵌套會將其他組件渲染到該組件內,而不是進行整個頁面跳轉router-view本身就是將組件渲染到該位置,想要進行頁面跳轉,就要將頁面渲染到根組件,在起始配置路由時候寫到:
var App = Vue.extend({ root });
router.start(App,'#app');
這里首先將根組件注冊進來,用於將路由中配置好的各個頁面渲染出來,然后將根組件掛載到與#app匹配的元素上。
組件的異步加載(按需加載組件)
在平時的demo中,你可能不會遇見這個需求,當頁面很多,組件很多的時候,你會發現你的頁面在首次加載的時候,異常的慢,這個是因為vue首次加載的時候把可能一開始看不見的組件也一次加載了,這個時候就需要對頁面優化了,就需要異步組件了。如何去寫異步組件呢,實際上很簡單,只需要在你的路由index,js里加上require就可以了,像下面這樣,這也是所謂的按需加載組件的實現原理。
vuejs構建組件使用
Vue.component('componentName',{ /*component*/ });
這里注意一點,組件要先注冊再使用,也就是說:
Vue.component('mine',{
template:'#mineTpl',
props:['name','title','city','content']
});
var v=new Vue({
el:'#vueInstance',
data:{
name:'zhang',
title:'this is title',
city:'Beijing',
content:'these are some desc about Blog'
}
});
如果反過來會報錯,因為反過來代表先使用了組件的,但是組件卻沒注冊。
webpack報錯后,使用webpack --display-error-details可以排錯
如何讓css只在當前組件中起作用
在每一個vue組件中都可以定義各自的css,js,如果希望組件內寫的css只對當前組件起作用,只需要在style中寫入scoped,即:
<style scoped></style>
vuejs循環插入圖片
在寫循環的時候,寫入如下代碼:
<div class="bio-slide" v-for="item in items">
< img src="{{item.image}}">
</div>
此時在控制台會出現警告
[Vue Warn]: src="{{item.image}}": interpolation in "src" attribute will cause a 404 request. Use v-bind:src instead.這里意思是在“src”屬性插值將導致404請求。使用v-bind:src代替。
所以替換成如下:
<div class="bio-slide" v-for="item in items">
< img v-bind:src="item.image">
</div>
這里需要主要,v-bind在寫的時候不能再用{{}},根據官方的說法:
<a v-bind:href="url"></ a>
這里 href 是參數,它告訴 v-bind 指令將元素的 href 特性跟表達式 url 的值綁定。可能你已注意到可以用特性插值href="{{url}}" 獲得同樣的結果:這樣沒錯,並且實際上在內部特性插值會轉為 v-bind 綁定。
綁定value到Vue實例的一個動態屬性上
對於單選按鈕,勾選框及選擇框選項,v-model綁定的value通常是靜態字符串(對於勾選框是邏輯值):
<!-- `toggle` 為 true 或 false -->
<input type="checkbox" v-model="toggle">
但是有時候想綁定value到vue實例的一個動態屬性上,這時可以用v-bind實現,並且這個屬性的值可以不是字符串。例如綁定Checkbox的value到vue實例的一個動態屬性:
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b">
<p>{{toggle}}</p >
這里綁定后,並不是說就可以點擊后由true,false的切換變為a,b的切換,因為這里定義的動態a,b是scope上的a,b,並不能直接顯示出來,此時
//當選中時
vm.toggle === vm.a
//當沒選中時
vm.toggle === vm.b
所以此時需要在data中定義a,b,即:
new Vue({
el:'...',
data:{
a:'a',
b:'b'
}
});
實現多個根據不同條件顯示不同文字的方法
v-if,v-else可以實現條件選擇,但是如果是多個連續的條件選擇,則需要用到計算屬性computed。例如實現當輸入框中什么都沒寫的時候顯示字符串‘empty’,否則顯示輸入框中的內容,代碼如下:
<div id="test">
<input type="text" v-model="inputValue">
<h1>{{changeVaule}}</h1>
</div>
new Vue({
el:'#test',
data:{
changeVaule:'123'
},
computed :{
changeVaule:function(){
if(this.inputValue!==''){
return this.inputValue;
}else{
return 'empty';
}
}
}
});
Vuejs在變化檢測問題
1.檢測數組
由於javascript的限制,vuejs不能檢測到下面數組的變化:
直接索引設置元素,如vm.item[0]={};
修改數據的長度,如vm.item.length。
為了解決問題1,Vuejs擴展了觀察數組,為它添加一個$set()方法:
// 與 `example1.items[0] = ...` 相同,但是能觸發視圖更新
example1.items.$set(0, { childMsg: 'Changed!'})
問題2,需要一個空數組替換items。
除了$set(),vuejs也為觀察數組添加了$remove()方法,用於從目標數組中查找並刪除元素,在內部調用了splice()。因此,不必:
var index = this.items.indexOf(item)
if (index !== -1) {
this.items.splice(index, 1)
}
只需:
this.items.$remove(item);
2.檢測對象
受ES5的顯示,Vuejs不能檢測到對象屬性的添加或刪除。因為Vuejs在初始化時候將屬性轉化為getter/setter,所以屬性必須在data對象才能讓Vuejs轉換它,才能讓它是響應的,例如:
var data = { a: 1 }
var vm = new Vue({
data: data
})
// `vm.a` 和 `data.a` 現在是響應的
vm.b = 2
// `vm.b` 不是響應的
data.b = 2
// `data.b` 不是響應的
不過,有辦法在實例創建之后添加屬性並且讓它是響應的。對於Vue實例,可以使用$set(key,value)實例方法:
vm.$set('b', 2)
// `vm.b` 和 `data.b` 現在是響應的
對於普通數據對象,可以使用全局方法Vue.set(object, key, value):
Vue.set(data, 'c', 3)
// `vm.c` 和 `data.c` 現在是響應的
有時你想向已有對象上添加一些屬性,例如使用 Object.assign() 或 _.extend() 添加屬性。但是,添加到對象上的新屬性不會觸發更新。這時可以創建一個新的對象,包含原對象的屬性和新的屬性:
// 不使用 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
關於vuejs頁面閃爍{{message}}
在vuejs指令中有v-cloak,這個指令保持在元素上直到關聯實例結束編譯。和CSS規則如[v-cloak]{display:none}一起用時,這個指令可以隱藏未編譯的Mustache標簽直到實例准備完畢。用法如下:
[v-cloak]{
display:none;
}
<div v-cloak>{{message}}</div>
這樣<div>不會顯示,直到編譯結束
關於在v-for循環時候v-model的使用
有時候需要循環生成input,用v-model綁定后,利用vuejs操作它,此時我們可以在v-model中寫一個數組selected[$index],這樣就可以給不同的input綁定不同的v-model,從而分別操作他們。這個我在demo中的dataBind.vue中用到。
vuejs中過渡動畫
在vuejs中,css定義動畫:
.zoom-transition{
width:60%;
height:auto;
position: absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
-webkit-transition: all .3s ease;
transition: all .3s ease;
}
.zoom-enter, .zoom-leave{
width:150px;
height:auto;
position: absolute;
left:20px;
top:20px;
transform: translate(0,0);
}
其中動畫在定的時候要注意上下對應,上面有什么,下面有什么,都要變化的,如果有不變化的,應該抽離出去,作為公共css樣式,在上面的css中,如果我只寫 transform: translate(-50%,-50%);而不寫下面的transform: translate(0,0);則會導致上面的transform: translate(-50%,-50%);被添加到下面,認為這個是不變的。
關於vuejs中使用事件名
在vuejs中,我們經常要綁定一些事件,有時候給DOM元素綁定,有時候給組件綁定。綁定事件在HTML中用v-on:click-"event",這時evet的名字不要出現大寫,因為在1.x中不區分大小寫,所以如果我們在HTML寫v-on:click="myEvent"而在js中寫myEvent就出錯誤,所以在vuejs的1.x綁定事件時候,要盡量避免使用大寫字母。在2.0中沒有該限制!
v-if與v-show的區別
v-if直接不渲染這個DOM元素,而v-show是會渲染DOM元素,只是使用display:none隱藏,打開開發者工具可以看到該DOM
關於transition全局鈎子如何在組件中使用
Vue.transition是定義一個全局transition鈎子的,如果想針對組件定義,則需要如下寫法:
export default{
transition:{
'fade':{
enter() {},
leave() {}
}
}
}
這樣fade這個過度鈎子只會作用於組件內,如果同時有同名的全局鈎子,則會優先使用組建定義的
利用vue-router如何實現組件在渲染出來前執行某個事件
export default{
data(){
return{
selected:0,
currentView:'view_0'
}
},
methods:{
choose(index) {
this.selected=index;
this.currentView='view_'+index;
}
},
route:{
data() {
/*每次切換路由,在渲染出頁面前都會執行*/
}
}
}
Vue運行報錯-。
[Vue-warn]: Missing required prop: "to" (found in component <router-link>) //報錯1
這個錯誤是<router-link>少了個to或者是寫錯
解決: 正確寫法為:<router-link to="/home">
路由在做字符串拼接的時候,to要作為一個屬性綁定 <router-link :to="'/home/'+item.id">
-。 聲明click/on-click的方法找不到
報錯
[Vue warn]: Invalid handler for event "on-click": got undefined //報錯1
解決: click/on-click的方法 沒有寫到methods:{ }里面。
給組件內的原生控件添加事件,不生效的問題
<!--比如用了第三方框架,或者一些封裝的內置組件; 然后想綁定事件-->
<!--// 錯誤例子01-->
<el-input placeholder="請輸入特定消費金額 " @mouseover="test()"></el-input>
<!--// 錯誤例子02-->
<router-link :to="item.menuUrl" @click="toggleName=''">
<i :class="['fzicon',item.menuIcon]"></i>
<span>{{item.menuName}}</span>
</router-link>
<!--上面的兩個例子都沒法觸發事件!!!-->
<!--究其原因,少了一個修飾符 .native-->
<router-link :to="item.menuUrl" @click.native="toggleName=''">
<i :class="['fzicon',item.menuIcon]"></i>
<span>{{item.menuName}}</span>
</router-link>
<!--明明官方文檔有的,一堆人不願意去看,,Fuck-->
<!--https://cn.vuejs.org/v2/guide/components.html#給組件綁定原生事件-->1234567891011121314151617181920
-。
在函數內用了this.xxx=,為什么拋出 Cannot set property 'xxx' of undefined;
這是this的套路了..this是和當前運行的上下文綁定的…
一般你在axios或者其他 promise , 或者setInterval 這些默認都是指向最外層的全局鈎子.
簡單點說:”最外層的上下文就是 window,vue內則是 Vue 對象而不是實例!”;
解決:
暫存法: 函數內先緩存 this , let that = this;(let是 es6, es5用 var)
箭頭函數: 會強行關聯當前運行區域為 this 的上下文;
關於this的知識, 推薦讀閱 <<你不知道的 JS 系列>>
兼容問題
使用了 axios, IE 整個家族都不支持 promise,
解決方案:
npm install es6-promise
// 在 main.js 引入即可
require("es6-promise").polyfill(); // ES6的polyfill
終於做完了第一個vue項目,本項目前端采用Vue,后端采用express+mongodb,Vue是使用vue-cli生成的項目。
開發期的問題如下:
(1)最好使用cnpm代替npm安裝依賴,因為開發過程中少部分包如果用npm是無法下載完成,一直卡住,換為cnpm毫無壓力記得加上--save選項,否則別人安裝的話會缺少包,linux下記得配置軟連接才能使用cnpm命令
(2)express如何設置favicon: 其實默認express已經引入了serve-favicon包,只需要使用app.use(favicon(__dirname+你的favicon路徑))即可
(3)百度地圖的引入需要異步加載,否則會報錯找不到BMap,具體見代碼
(4)非父子組件通信新建一個Vue實例作為通信中轉站,可以很方便的處理大部分非父子通信邏輯
(5)引入reset.css是在main.js中引入
(6)es6的import規則:如果路徑最終是一個文件夾,則會首先觀察文件夾下是否有 package.json ,如果有 package.json 則會去加載 main 字段指向的文件,如果沒有 package.json ,則會在這個文件夾下尋找 index 文件並加載。所以main.js里面加載router直接這樣寫 import router from './router'
(7)為了處理某些瀏覽器兼容,比如ie,safari,必須引入babel-polyfill才行,在main.js里加上import 'babel-polyfill',否則ie和safari會不認識es6的語法
(8)main.js里面Vue實例的template:"<App/>"的意思是將app組件替換掉#app的那個div,同時App也是一個組件,在components里面必須要聲明
(9)vuex狀態管理器的使用最好按照官網那一套來寫,比較規范
(10)前端路由驗證登錄情況可以在路由里加上meta參數,如下圖,然后router.beforeEach里判斷requireAuth是否為真
(11)組件里面data使用圖片路徑是要用require('./../mm.png')才能正確訪問圖片,這樣才能夠被webpack正確解析
(12)下拉框最好不用原生的,不同瀏覽器表現不同,得自己寫一套才行
(13)server端express自帶前端部分,在views里面,注意設置engine解析,是html的引擎,引入ejs包
app.set('views', path.join(__dirname, 'views'));
app.engine('.html',ejs.__express);
app.set('view engine', 'html');
(14)開發環境下我的項目路徑下有中文,結果jieba分詞包執行的時候報錯:找不到jieba.node,然后將路徑改為英文問題解決,得出結論路徑別加中文,指不定啥問題會出現
部署服務器出現的問題:
(1)首先express默認設置的是3000端口,所以多個項目部署的話得修改bin/www里面的port值,改為不一樣的
(2)線上部署首先得把server文件夾內的所用內容拷貝到阿里雲服務器上,新建一個文件夾來存放。阿里雲服務器必須得設置安全組,目的是將開放訪問的端口,否則端口無法訪問,比如express設置了4000端口,阿里雲這里就得設置4000端口開放
(3)服務器環境搭建:首先安裝node,然后安裝mongodb,注意服務器上的mongodb必須設置訪問權限,否則非常不安全,同時server端連接mongo的語句也得加上用戶名和密碼
(4)代碼拷貝后執行cnpm install 安裝依賴,安裝完成node www啟動項目,此時遇到問題如下圖,可以看出此時的node不支持async函數,也就是說node版本太低了,但是我本地測試沒問題,查詢node版本后發現,本地node是v8.3.0,服務器上是v6.4,因此要升級node,具體方法為:1,安裝n模塊 npm install -g n,這個模塊專門管理node版本的,2,n stable此命令可升級到最新穩定版,n v8.3.0這個命令可升級到指定版本。我將服務器上的node升級到和本地一樣后問題解決了
(5)由於本項目使用了selenium-webdriver自動化測試工具當做爬蟲(原因是某些網站主內容在iframe內,一般爬蟲無法獲取內容,selenium內有switchToFrame方法切換frame從而獲取到內容),本地測試時這個工具運行時會自動打開chrome(事先設置了chrome參數),但是放到阿里雲服務器上時報錯找不到chrome binary,查詢后發現必須安裝chrome瀏覽器,具體鏈接http://www.linuxidc.com/Linux/2016-05/131096.htm 然后繼續啟動發現仍然報錯:chrome failed to start. 仔細分析后發現這是由於沒有設置headless參數導致的,由於阿里雲服務器沒有界面,必須設置啟動方式為headless無頭,同時記得添加參數no-sandbox,不添加則chrome仍然無法啟動,以下代碼在官網上在找到的。
(6)前端打包后將dist內的文件拷貝到server里面views目錄下即可,然后啟動express即可訪問頁面,但是頁面里面出現許多png not found,檢查后發現是路徑問題,這些找不到的png的路徑都是寫在css里面,由於打包后路徑發生改變,所以找不到。解決方法是修改前端build/util.js 里面的路徑,添加publicPath參數
未解決的問題如下:
(1)開發環境下訪問第三方網站必須做跨域處理,為啥生產環境下可以不用跨域?直接這樣寫就行
(2)
vue 項目搭建
前言
基於Vue.js 2.x系列 + Element UI 的組件化開發方案,vue.js不支持IE8及其以下版本,因為 Vue.js 使用了 IE8 不能模擬的 ECMAScript 5 特性。 Vue.js 支持所有兼容 ECMAScript 5 的瀏覽器。vue的安裝依賴於node.js,要確保你的計算機上已安裝過node.js。可進入cmd編輯器,輸入命令 node -v進行查看。node盡量要用新一些的版本,否則后續安裝會提示node版本過低。去node官網下個新版的node重新安裝就可以。
安裝步驟
1.全局安裝node.js
下載地址:https://nodejs.org/en/download/
安裝成功后可進入cmd編輯器,輸入命令 node -v進行查看,出現版本號說明安裝成功,npm是包管理工具,noed安裝好就有npm指令了
C:\Users\user>node -v
v8.4.0
C:\Users\user>
2.全局安裝webpack 和 vue-cli
npm install -g webpack
npm install -g vue-cli
3.根據vue-cli構建項目
vue init webpack my-vue //創建一個項目,名稱my-vue1
cd my-vue // cd 到my-vue的根目錄下1
npm install // 下載項目依賴1
npm run dev // 讓該項目在本地上跑起來1
這樣一個基礎的vue項目目錄就自動會展現在你面前,我們可以來看一下其自動生成的基礎文件:
項目結構簡單介紹
├── build // 是一些webpack的文件,配置參數什么的,一般不用動
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config // 環境配置文件,是vue項目的基本配置文件
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── node_modules // npm包文件 是項目中安裝的依賴模塊
├── src // 源碼文件夾,基本上文件都應該放在這里。
│ ├── assets //資源文件夾,里面放一些靜態資源
│ │ └── logo.png
│ ├── components //這里放的都是各個組件文件
│ │ └── Hello.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue //App.vue組件
│ └── main.js //入口文件
├── static //生成好的文件會放在這個目錄下。
├── .babelrc // babel配置文件 , vue開發需要babel編譯
├── .gitignore // gitignore忽略文件
├── .editorconfig // 編碼風格配置文件
├── .postcssrc.js // postcss配置文件
├── package.json // node包管理文件
├── index.html // 首頁模板
├── package.json // 包管理文件
└── README.md // 描述文件123456789101112131415161718192021222324252627282930313233
main.js文件介紹
這里是入口文件,可以引入一些插件或靜態資源,當然引入之前要先安裝了該插件,在package.json文件中有記錄。
/*引入Vue框架*/
import Vue from ‘vue’
/*引入資源請求插件*/
import VueResource from ‘vue-resource’
/*重置樣式*/
import “assets/css/base.css”
/*基本JS*/
import “assets/js/common.js”
/*引入路由設置*/
import “./routers.js”
/*使用VueResource插件*/
Vue.use(VueResource)
App.vue
這是一個標准的vue組件,包含三個部分,一個是模板,一個是script,一個是樣式,這里需要了解vue的基礎。
import Hello from './components/Hello'
export default {
name: 'app',
components: {
Hello
}
}
app {
font-family: ‘Avenir’, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
vue-route
作用:
通過管理url實現url和組件的對應,
通過url進行組件之間的切換
必須引入router組件 是單獨文件
Hello App!
Go to Foo
Go to Bar
axios使用
## 安裝 ##
npm install axios vue-axios -D
在main.js 入口文件里面寫
import Axios from ‘axios’
import VueAxios from ‘vue-axios’
Vue.use(VueAxios,Axios)
在其它組件里面調用
Javascript
export default {
name: 'blog',
created () {
this.getGoods()
},
methods: {
getGoods () {
this.$http.get('http://lc.shudong.wang/api_goods.php')
.then((res) => {
console.log(res.data)
})
.catch((error) => {
console.log(error)
})
}
}
}
## vue UI組件
使用目的:
提高開發效率
直接拿過來用
elementUi
安裝
npm ielement-ui -D
## 引入 main.js 入口文件 ##
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
Vue.use(ElementUI)
hipv2頭痛的問題
1、先操作element分頁,切換到第2頁
模擬畫面數據重新查詢,從代碼里面將current-page強制設置為1(即重新查詢數據,並將當前頁重置為第1頁)
此時畫面顯示是對的,分頁組件已經將第1頁的頁碼數字激活了
然后點擊第2頁數字,進行換頁
畫面顯示也是對的,第2頁數字變成激活狀態,但是此時竟然無法觸發current-change事件
個人猜測,其內部邏輯也許還認為當前頁是第2頁,所以沒有再次觸發current-change
這樣會導致數據重新加載后,分頁出現bug,無法加載分頁數據
解決分頁BUG的辦法
1、使用v-if強制刷新組件即可組件聲明改成:(data里面初始化paginationShow為true)
<el-pagination
v-if="paginationShow"
:current-page.sync ="currentPage"
background @current-change="pageChange"
:page-size="perPage"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
2、把search方法改成下面這樣:
search () {
this.paginationShow = false
// 模擬ajax查詢數據處理,查詢成功后執行下面代碼
this.total = res.data.total;
this.perPage = res.data.pageSize;
this.$nextTick(function () {
this.paginationShow = true
})
}
3、注意事項:
paginationShow = true,重新顯示分頁,必須放到$nextTick,畫面刷新完畢后,不然無效果1234567891011121314151617181920212223242526272829
后期項目優化
第一次使用vue2.0開發,在使用vue-cli腳手架打包后(UI用的Element-ui)(請求模塊用的時axios),發現vendor文件很大,2M左右。。后來翻閱資料才明白,原來webpack把所有的庫都打包到了一起,導致文件很大。
我的優化方法:
方法1:
1.把不常改變的庫放到index.html中,通過cdn引入,比如下面這樣:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- <meta base="/MBSS/"> -->
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon " type="images/x-icon" href="/static/img/bitbug_favicon.ico">
<!-- <link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css"> -->
<title>和佳醫院信息集成平台</title>
<!-- 先引入 Vue -->
<!--開發環境-->
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
<!--生產環境-->
<!--<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>-->
<!-- 引入組件庫 -->
<script src="https://cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.3.3/index.js"></script>
<script src="https://cdn.bootcss.com/echarts/4.0.4/echarts-en.common.js"></script>
<script src="./static/js/config.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>12345678910111213141516171819202122232425262728
2.然后找到build/webpack.base.conf.js文件,在 module.exports = { } 中添加以下代碼:
externals:{
'vue': 'Vue',
'element-ui': 'ELEMENT',
'echarts':'echarts',
'axios':'axios'
}123456
3.在mian.js中import … from …就可以去掉了,若沒去掉webpack還是會把對應的依賴進行打包。
這樣webpack就不會把vue.js, echarts, element-ui, axios庫打包了。聲明一下,我把main.js中對element的引入刪掉了,不然我發現打包后的app.css還是會把element的css打包進去,刪掉后就沒了。
然后你打包就會發現vendor文件小了很多~
如果你還不滿足,請接着往下看·····
方法2:
vue路由的懶加載(具體作用,官網查看,這里就不多介紹了)。
剛開始我們使用路由可能是下面這樣(router.js),這樣一開始進入頁面就會把所有的路由資源都加載,如果項目大,加載的內容就會很多,等待的時間頁就會越長,導致給用戶的不好的體驗效果。
import login from '../components/login/login.vue'
import Index from '../components/stystemm/index/index.vue'
import home from '../components/stystemm/home/home.vue'
import ImageList from "../components/stystemm/ImageList/ImageList.vue"
export default new Router({
routes:[
{
path:'/',
component: hospital
},
{path:'',component:home},
{path:'/imageList',component:ImageList}
}
]
})12345678910111213141516
為了把路由分模塊,然后每次進入一個新頁面才加載該頁面所需要的資源(也就是異步加載路由),我們可以像下面這樣使用(router.js):
1.vue異步組件寫法:
export default new Router({
routes: [{
path: '/',
name: 'Login',
component: resolve => require(['../components/page/Login.vue'],resolve) ,
},
{
path: '/empi',
name: 'eMPI管理',
component: resolve => require(['../components/common/index.vue'],resolve),
children: [{
path: '/empi/summary',
name: '索引總覽',
component: resolve => require(['../components/page/eMPI/pandect/pandect.vue'],resolve)// empi - 總覽
},
{
path: '/empi/patient',
name: '患者信息',
component: resolve => require(['../components/page/eMPI/PatientInformation/PatientInformation.vue'],resolve)
}
]
},
{
path: '/role',
name: '404',
component:resolve => require(['../components/page/404.vue'],resolve)
}
]
});
123456789101112131415161718192021222324252627282930
2.es提案的import()寫法:
// 下面2行代碼,沒有指定webpackChunkName,每個組件打包成一個js文件。
/* const Home = () => import('@/components/home')
const Index = () => import('@/components/index')
const About = () => import('@/components/about') */
// 下面2行代碼,指定了相同的webpackChunkName,會合並打包成一個js文件。 把組件按組分塊
const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')
const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index')
const About = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/about')
{
path: '/about',
component: About
}, {
path: '/index',
component: Index
}, {
path: '/home',
component: Home
}12345678910111213141516171819
3.webpack提供的require.ensure() vue-router配置路由,使用webpack的require.ensure技術,也可以實現按需加載。這種情況下,多個路由指定相同的chunkName,會合並打包成一個js文件。
{
path: '/home',
name: 'home',
component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
}, {
path: '/index',
name: 'Index',
component: r => require.ensure([], () => r(require('@/components/index')), 'demo')
}, {
path: '/about',
name: 'about',
component: r => require.ensure([], () => r(require('@/components/about')), 'demo-01')
}12345678910111213
方法3:
在項目config/index.js中可以開啟gzip壓縮,對打包優化也有很大的幫助
1.首先安裝插件 compression-webpack-plugin
cnpm install --save-dev compression-webpack-plugin1
2.設置productionGzip: true
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report123456789101112
3.npm run build執行后會發現每個js和css文件會壓縮一個gz后綴的文件夾,瀏覽器如果支持g-zip 會自動查找有沒有gz文件 找到了就加載gz然后本地解壓 執行。
---------------------
首頁頭部欄的搜索功能,全局搜索。
直接跳轉查詢界面,然后這個很簡單,輸入框回車事件push到查詢界面,然后將值一塊兒帶過去,初始化查詢界面。
可是問題來了,現在查詢這個組件里繼續輸入然后回車。這時候路由是沒變化的,組件里的鈎子也不會再次執行。怎么監測到搜索內容變化呢。
首先想到的是監測路由數據變化,后來發現太low,
再次想到的是監測vuex數據,搞了半天還是不行。
最后使用了非父子組件傳值,
建立一個bus文件,文件建一個bus.js
內容就是引入vue,建立一個空的vue實例。
在頭部搜索欄的組件里引入bus,用bus.$emit()觸發一個事件傳值,也就是搜索框的回車時觸發。在查詢界面引入bus,之后bus.$on()得到數據變化。然后做出相應的操作。
這里要注意一定不能使用bus.$(‘value',function(val){});因為這樣的話,你的函數里的this會指向空實例bus。所以我們必須使用es6的寫法,val => {};這樣的話this會指向當前查詢界面的實例。
vue打印功能,window.open(url}這個東西很多bug。第一用戶體驗,用戶必須允許否則就無法彈出,第二響應時間超過一定時間則無法彈出。
解決方法:
跳轉一個指定界面,然后重定向。
var newWindow= window.open('')
setTimout(()=>{
newWindow.location.href = '新的url'
},3000);
軟刪除數據如何過濾
我們的網站刪除如果是后台數據庫已有數據是不能硬刪除的,只能修改某個字段然后標記為刪除,叫做軟刪除。
這樣的話我們的前端界面渲染時就需要過濾掉你剛剛刪除的數據
我的項目里是用的table然后給了tableData | filterData
filterData:
var newData = [];
for(let key of tableData){
if(key.removed !== 1){ newData.push(key) }
return newData
}
簡單解釋:就是全局過濾器獲取原數據,然后如果某個字段是已經刪除了,也就是軟刪除改變了字段的數據,就不管他了,其他的就生成一個新數組然后返回。
共用界面如何刷新
項目里一個費用申報單界面是共用的,很多地方過來查看和編輯也是這個路由這個組件,只是很多input的內容是空或者是不能編輯只能看。
問題來了,你來到這個界面后查看完了突然想我想現在編輯一個申報單,可是此時此刻你點擊路由這個組件是不會更新的,因為vue覺得你已經在這個界面了,他是不會理你的。
使用過幾個路由鈎子,什么routerberfor……,什么放在app.vue里,都只能檢測到路由離開進來的變化,而無法就在當前路由點擊當前路由的辯護。
解決方法使用watch 監測路由
//監視路由變化,然后初始化界面。
watch: {
'$route' (to, from) {
alert('路由改變了');
}
}
路由配置:
path:urlal
不管是插件還是你自己寫的頭部菜單,都一定有一個事件監聽到你點擊當前路由。在這個事件里我們來處理
let val = new Date();
this.$router.push({
path: url/val
})
這樣你就可以在你的組件里監聽到路由變化了。然后也就可以做出相應的操作。原理就是利用參數的變化唄,時間new出來的永遠都是不一樣的。
表單重置
項目中很多時候界面的數據不需要清除,但是一些操作會觸發驗證事件,然后一串紅字你不達到條件就不會消失,這時候很難受,所以就有了重置表單的驗證。
關於表單重置,我使用的是element-ui自帶的resetFields()事件。
我們唯一要注意的是,不能再created()鈎子中使用,因為this.$refs[formname]中的ref,一定要加載完后才能找到的。否則會報錯說你resetFields()事件未定義。所以一定要在mounted()鈎子中使用。
require('!style-loader!css-loader!less-loader!./assets/css/common_m.less')
PS:在webpack中配置好了loader之后,不需要在引入文件時寫loader和文件后綴。
cssloader:
{
test: /\.css$/,
exclude: /node_modules/,
loader: "style-loader!css-loader",
},
{
test: /\.less$/,
exclud: /node_module/,
loader: "style-loader!css-loader!less-loader",
},
// <router-view></router-view>與寫的位置無關
path.join(__dirname,'..');
this.$set(this, '屬性', '值')
css scoped 僅本組件
對象可以存圖片。使用require即可。
images: {
error: require('./toast-error.png'),
success: require('./toast-success.png'),
load: require('./toast-load.gif'),
waiting: require('./toast-hourglass.svg')
}
border。各屏幕分辨率不同出現的粗細問題。
公共部分。寫成組件。利用傳參改變樣式/內容。
Ajax。axios。
跨域問題的解決:1.服務器端協助配置。2.jsonp。3.devServer配置代理。
能用data數據解決的問題,不用方法解決。如取反。長度。是否空/false/true。(Vue是數據驅動而不是結構驅動)
v-bind:style src to ...
路由傳參。$route。
歷史記錄切換。router-link to/:to或者router.push()/router.replace()
#router.push(location) 導航到不同的 URL
// 聲明式
<router-link :to="...">
// 編程式
router.push(...)
// 字符串
router.push('home')
// 對象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
#router.replace(location) 它不會向 history 添加新記錄,而是替換掉當前的 history 記錄。
// 聲明式
<router-link :to="..." replace>
// 編程式
router.replace(...)
#router.go(n) 在 history 記錄中向前或者后退多少步,類似 window.history.go(n) 值可以正可以負
#綜合使用
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
router.push({ name: 'user', params: { userId: 123 }})
這兩種方式都會把路由導航到 /user/123 路徑。
// 命名視圖
// https://router.vuejs.org/zh-cn/essentials/named-views.html
// https://jsfiddle.net/posva/6du90epg/
// 有時候想同時(同級)展示多個視圖,而不是嵌套展示,例如創建一個布局,有 sidebar(側導航) 和 main(主內容) 兩個視圖,這個時候命名視圖就派上用場了。
// 你可以在界面中擁有多個單獨命名的視圖,而不是只有一個單獨的出口。如果 router-view 沒有設置名字,那么默認為 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
// 一個視圖使用一個組件渲染,因此對於同個路由,多個視圖就需要多個組件。確保正確使用 components 配置(帶上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
重定向 和 別名
切換時清空歷史記錄。數據丟失。sessionStorage。
相對路徑。
append。concat。
渲染成某個標簽。tag
可以在router-link內嵌套其他內容。
類型都是Object
$route.params(一個 key/value 對象,包含了 動態片段 和 全匹配片段,如果沒有路由參數,就是一個空對象。)
$route.query(一個 key/value 對象,表示 URL 查詢參數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1,如果沒有查詢參數,則是個空對象。)
<div>
<router-link :to="{ name: 'Hello' , params: { userId: 123 }}">
click1
</router-link>
</div>
<div>
<router-link :to="{ path: 'hello' , query: {show: 2}}">
click2
</router-link>
</div>
console.log(this.$route.params.userId)
console.log('top is params, bottom is query')
console.log(this.$route.query.show)
watch:
watch: {
'$route' (to, from) {
// 對路由變化作出響應...
}
}
v-show和v-if 。v-if為假直接不渲染。v-show為假display none。
當 v-if 與 v-for 一起使用時,v-for 具有比 v-if 更高的優先級。
個人建議:外容器overflow hidden,內容器overflow scroll。
過渡效果:
<transition name="fade"></transition>
.fade-enter-active, .fade-leave-active {
-webkit-transition: opacity .7s;
transition: opacity .7s;
}
.fade-enter, .fade-leave-active {
opacity: 0
}
路由鈎子:判斷從哪個路由過來,到哪個路由。然后跳轉到哪個路由。(全局鈎子。局部鈎子。)
component頁面內也可以監聽路由。
滾動行為。歷史記錄之間切換。頁面位置。返回對象。
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
錨點的實現。通過scrollTop。或滾動行為。
路由元信息。路由嵌套。
懶加載。
在異步加載頁面中載嵌入異步加載的組件時對頁面是否會有渲染延時影響?
答:會, 異步加載的組件將會比頁面中其他元素滯后出現, 頁面會有瞬間閃跳影響;
解決方案:因為在首次加載組件的時候會有加載時間, 出現頁面滯后, 所以需要合理的進行頁面結構設計, 避免首次出現跳閃現象;
At all:
1、路由頁面以及路由頁面中的組件全都使用懶加載
優點:(1)最大化的實現隨用隨載
(2)團隊開發不會因為溝通問題造成資源的重復浪費
缺點:(1)當一個頁面中嵌套多個組件時將發送多次的http請求,可能會造成網頁顯示過慢且渲染參差不齊的問題
2、路由頁面使用懶加載, 而路由頁面中的組件按需進行懶加載, 即如果組件不大且使用不太頻繁, 直接在路由頁面中導入組件, 如果組件使用較為頻繁使用懶加載
優點:(1)能夠減少頁面中的http請求,頁面顯示效果好
缺點:(2)需要團隊事先交流, 在框架中分別建立懶加載組件與非懶加載組件文件夾
3、路由頁面使用懶加載,在不特別影響首頁顯示延遲的情況下,根頁面合理導入復用組件,再結合方案2
優點:(1)合理解決首頁延遲顯示問題
(2)能夠最大化的減少http請求, 且做其他他路由界面的顯示效果最佳
缺點:(1)還是需要團隊交流,建立合理區分各種加載方式的組件文件夾
export default new Router({
routes: [
{
path: '/',
component: resolve => require(['components/Hello.vue'], resolve)
},
{
path: '/about',
component: resolve => require(['components/About.vue'], resolve)
}
]
})
// solve:
// components/page
<keep-alive> 包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。和 <transition> 相似,<keep-alive> 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現在父組件鏈中。
當組件在 <keep-alive> 內被切換,它的 activated 和 deactivated 這兩個生命周期鈎子函數將會被對應執行。
主要用於保留組件狀態或避免重新渲染。
組件:
當注冊組件(或者 props)時,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不關心這個。
component命名要求:
1. 檢查名稱是否與 HTML 元素或者 Vue 保留標簽重名,不區分大小寫。(常用HTML標簽)
2. 檢查組件名稱是否以字母開頭,后面跟字母、數值或下划線。
使用 Virtual DOM 解析模板時,不會將模板中的標簽名轉成小寫,而是保留原始標簽名。然后,使用原始的標簽名進行匹配組件。
例如, <MyComponent></MyComponent> 不會轉為為小寫形式,直接以 MyComponent 為基礎開始匹配。
當然,匹配的規則依次匹配:原標簽名、camelCase化的標簽名、PascalCase化的標簽名。
computed,內容變時會更新,否則會緩存起來。
如果是傳參等方式得到值,需要判斷值是否為空,否則會報值為undefined的錯。
vue項目中,不用寫CSS的兼容性代碼。
因為vue-loader在編譯.vue文件的時候,使用了Postcss的工具,它會給有兼容性問題的屬性添加兼容性代碼。
它是根據can i use官網寫的代碼。
寫在<style></style>內才會生效。在html中添加style屬性是不會添加兼容性代碼的。
換行問題:
v-html可以直接加<br>進行換行,v-model中無效
例外:textarea標簽使用v-model時,可以使用\r進行換行
ref的使用:
在父頁面調用子組件的標簽:
在父頁面中的組件上加ref。然后在子組件中的某個標簽加ref。即可。
nextTick
將回調延遲到下次 DOM 更新循環之后執行。
https://cn.vuejs.org/v2/api/#vm-nextTick
new Vue({
// ...
methods: {
// ...
example: function () {
// 修改數據
this.message = 'changed'
// DOM 還沒有更新
this.$nextTick(function () {
// DOM 現在更新了
// `this` 綁定到當前實例
this.doSomethingElse()
})
}
}
})
box-sizing: border-box
:disabled="type == '值'"
:class = name
name可以是compute
getBoundingClientRect用於獲取某個元素相對於視窗的位置集合。集合中有top, right, bottom, left等屬性。
點擊事件。取消冒泡。
v-on:click.stop="doThis"
阻止默認事件。
v-on:click.prevent="doThis"
https://cn.vuejs.org/v2/guide/events.html#事件修飾符
組件內部的方法,命名建議以兩個下划線開頭
外部傳參方法等,不用兩個下划線開頭。
padding取%值,
> %,規定基於父元素的寬度的百分比的內邊距。
給按鈕加padding。方便點擊
或者整個部分可以點擊。
export 和 export default
http://es6.ruanyifeng.com/#docs/module#export-命令
http://es6.ruanyifeng.com/#docs/module#export-default-命令
切換組件時,希望保留組件的狀態時,加上keep-alive 即可
個人做vue項目的心得和體會
vue.js
首先,vue.js是一個數據驅動視圖的一個js框架,操作數據,然后實時反應到dom元素上的一個動態視圖框架,它也是一個漸進式開發框架,比如,我用vue-cli搭建了一個本地開發環境,用nodejs跑了起來,當把vue引進來之后,要做項目還是遠遠不夠的,就用到了vue全家桶
比如
UI框架 : element-ui
http請求工具:axios(個人推薦用這個)
cookie工具:vue-cookie
構建工具:vue-cli
路由插件:vue-router
狀態管理工具:vuex
滾動條插件:vuescroll
兼容IE的插件:babel-polyfill(當然vue只兼容到IE9+)
如果平時項目中你用到了哪些框架及插件,就可以通過npm install,下載下來,引入到項目中去就ok了
個人覺得項目目錄結構可以是這個樣子的
主要是src文件夾,assets主要用於放一些項目中的圖片等之類的,compoents就放一些自己定義的組件,pages就放頁面,當然都是vue文件啦,router放路由配置,server放一些靜態目錄什么的,放一些常量什么的都可以,store,熟悉vuex的小伙伴應該都知道,沒錯,就是放vuex的
通過用vue做了一些東西后,引發了我的一些思考,也算是心得吧,跟我寫代碼的風格有關系,也許你覺得我說的這些不好,你可以繼續延用你的風格,哈哈,畢竟敲代碼都有自己獨特的風格嘛,哪個程序猿還沒有點傲嬌的小脾氣呢!
關於使用UI框架
一般我都是用element-ui這個ui框架,其中有一些ui組件特別好用,比如$message,提示組件,不用自己寫,直接調用,就能用於增加,修改的一些功能,比如提示新增成功,修改成功等,還有按鈕組件,彈出框組件等一些基本的組件,對於我自己寫的其他代碼后續幾乎沒有沖突,且靈活度高的,我就直接用了框架內的組件,但是一些可控性不強,且不夠靈活,幾乎不能改動的組件我就會選擇自己寫,比如樹形控件,這個樹形控件的樣式特別單一,而且不可控,而且靈活度不高,我不是很喜歡,就利用vue的遞歸組件自己寫樹形控件,感興趣的小伙伴可以看下我下篇博客,馬上就發《vue如何用遞歸組件寫樹形控件》,總結一句話,靈活度高,就用框架,靈活度不高,就自己寫,自己寫的可控性很強!
關於封裝組件
有時候項目中經常會碰到這種情況,好幾個頁面的列表頁除了表頭不一樣,幾乎樣式是一模一樣的,這樣的你就可以封裝一個列表組件了,但是只適用於樣式差不多的列表,如果你覺得都封裝進一個組件里面,對后續開發的一些可控性不是很強,而且每個頁面的需求都不一樣,處理方式和方法都不很一樣,那么我勸你還是老老實實自己寫列表吧,因為你封裝到一個組件中,對於每個頁面的處理方式都要進行判斷,這樣會顯得這個組件很臃腫,我覺得還不如分開寫,所以是否要把一個功能模塊封裝進組件,靈活把握就可以了
關於一些目錄和http請求
關於一些目錄,比如導航目錄,一般是寫死的,當然也有從后台獲取的,我現在這個項目就是從后台獲取目錄,還有請求URL,和一些http請求,你就可以新建一個server目錄,然后分模塊的寫道js文件夾里面,return出去,然后再在其他頁面中引入,這樣做的好處是,模塊化管理,將常量和http請求進行統一管理,比如下圖
我就把http請求進行了統一管理,然后暴露出去,將來有改動我就可以直接改這里就可以了
關於vuex
下面說一說vuex,也許有許多小伙伴對於這個都不知道干嘛用的,根據我的理解,vuex並不是那么神秘,就是一個公共狀態庫,是互不聯系的兩個組件的通信橋梁,比如我又一個列表組件1,又有一個列表組件2。這兩個組件完全不一樣,絲毫沒有聯系的那種,但是它們之間需要通信,比如都需要根據一個布爾值show來進行顯示還是隱藏,這樣就可以把這個show放進vuex狀態庫里面,當這個show改變的時候,這兩個列表組件就可以根據show的變化自動的進行顯示隱藏了
關於http請求寫在哪里的問題
我一開始接觸vuex的時候,vuex里面有一個actions,既可以進行同步操作,又可以進行異步操作,官網上是推薦將http異步請求放在actions里面,因為當時剛學,沒有判斷能力,就按照官網上來寫,在vue文件里面只寫邏輯代碼,而把所有的http異步請求全部寫在了actions里面,這樣問題就來了,首先mutations里面的函數必須要用它來提交,而且vue里面通過引入import { mapGetters , mapActions} from 'vuex',來獲取到函數和狀態,我發現寫一個http需要寫好多代碼,我如果直接寫道methods里面,就特別簡單,后來我就在想到底放哪里簡單些,綜合了網上的一些看法,我覺得這跟個人代碼風格有關系,官網是推薦寫到actions里面,可是如果一個頁面里面的一些http請求只是自己用,也不跟其他頁面以及組件進行通信的話,我覺得沒有必要寫到vuex里面,寫在methods里面反而簡單些,反正到最后項目的數據都能流通就可以了,我覺得一些http請求得到的數據要和其他組件或頁面共用的話,我覺得必須要放在vuex里面,這是最棒的辦法,如果是自己在自己的小窩里小打小鬧,也不影響別人,那么我們還是寫在methods里面吧,畢竟別人又不用,為什么要把它驅逐家門呢,哈哈!!
就說到這里吧
Vue項目經驗
動態添加class或style
有時候希望根據某個狀態值來決定是否添加某個類,可以這樣:
// 數組語法
<div :class="['a',value==true?'b':'']"> //類a總是存在,當value為真時類b存在
// 對象語法
<div :class="{'a':true,'b':value}"> //類a,b都由value控制
<div :style="{background:'url('+value.url+')'}"> //對於style,最好用對象語法,屬性值取自當前vue實例
<div className={style.root} style={{ background: `url(${graduateCelebrate}) center center/ 100% 100% no-repeat content-box` }}>
綁定事件函數
有兩種方式:
// 方式1
<div @click='handle'></div> //handle函數的this指向當前vue實例,並且會將event對象傳入handle作第一個參數,通常可用event.target獲取被點擊的DOM元素
// 方式2
<div @click='handle("param1",$event)'></div> //handle函數的this指向當前實例,也可將$event傳入handle,作用同上,一般用來獲取被點擊的DOM元素
關於箭頭函數
vue的一些配置項,如mounted,beforeRouteEnter,beforeRouteLeave等,本身是一個函數,若使用箭頭函數語法,則其內的this對象不是當前vue實例。因此這三個配置項不要用箭頭函數,其它的配置項,如methods,computed,watch等接受對象作為配置的,其內的函數最好用箭頭函數,另外setTimeout(fn,time)中的fn最好也用箭頭函數。
watch對象變化:
'messages': { // messages是一個數組,監聽數組或對象的變化
handler: function(newValue, old) {
if (newValue.length == 0) {
setTimeout(() => {
history.back();
}, 1000)
}
},
deep: true
}
關於beforeRouteEnter和beforeRouteLeave
beforeRouteEnter(to,from,next) 無法訪問當前vue實例,一般用於從localStorage中讀取數據恢復頁面,比如從頁面a到b,在從b回到a,若a有數據變化(ajax拉取)就更新頁面,否則不更新;或者還原當前滾動條位置
beforeRouteLeave(to,from,next) 可訪問當前vue實例,一般用於頁面離開時將某些狀態保存在localStorage中,例如當前滾動條位置
二者都必須顯示調用next函數
keep-alive和router-view混合使用
// 這是目前用的比較多的方式
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
...
routes: [
{ path: '/', redirect: '/index', component: Index, meta: { keepAlive: true }},
{
path: '/common',
component: TestParent,
children: [
{ path: '/test2', component: Test2, meta: { keepAlive: true } }
]
}
....
// 表示index和test2都使用keep-alive
若有三個頁面A->B->C,希望A->B時B拉新數據;C返回B時B不拉新數據,且保存B到C之前的位置,該如何做?
1、給B設置keepAlive:true;
2、在B的beforeRouteEnter中做獲取數據的操作,該鈎子的三個參數可以判斷B的上一個頁面,從而決定是否拉數據;
在B的beforeRouteLeave里用localStorage記錄位置,並清除和頁面展現相關的數據狀態;
3、在接口回調里最好用EventCenter這種觀察者模式來觸發數據獲取成功的事件
4、在B的mounted鈎子里接受數據獲取成功的事件,就可以拿到數據
5、在B的activated鈎子里取localStorage里的位置
router-link常用方式
<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<!-- 帶查詢參數,下面的結果為 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
//對應js
this.$route.params.userId
this.$route.query.plan
過度動畫
單元素過渡
transition包一個含有v-if或v-show指令的元素就行
動畫過程
多元素過渡
transition下只能有一個根元素,所以多元素時必須用v-if,v-else來保證只有一個,此外,多個相同標簽時,最好給每個標簽設置key
多組件過渡
用動態組件即可
列表過渡
需用transition-group組件包裹,被包裹元素必須設置key,其他和單元素一樣
數據拉取
數據獲取操作最早可以放在每個組件的beforeRouteEnter里,然后利用觀察者模式把數據傳到beforeMount鈎子函數里做邏輯操作
beforeRouteEnter(to, from, next) {
let feed_id = to.params.id;
if (from.name != 'CommentDetail' && feed_id) {
getArticleDetail({
feed_id: feed_id
}).then(res => EventCenter.$emit('renderArticle', res));
getComment({
feed_id: feed_id
}).then(res => EventCenter.$emit('renderComment', res));
}
next();
}
beforeMount(){
// 1、渲染文章詳情
EventCenter.$on('renderArticle', res => {
});
// 2、渲染文章評論
EventCenter.$on('renderComment', res => {
})
}
插件 or 組件
對於全局使用頻率較高的最好寫成插件,而不是組件。例如loading,toast之類,若寫成組件,則需要在每個父組件中引入該組件,比較麻煩,寫成插件就可以直接調用
import $ from 'n-zepto';
import Vue from 'vue';
export default {
install() {
var timer = null;
Vue.prototype.$alerTip = function(text, delay, options) {
var defaultCssObject = {
position: 'fixed',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 10000,
minWidth: '1.5rem',
margin: '0 auto',
left: 'calc((100% - 1.5rem) / 2)',
bottom: '1.4rem',
borderRadius: '.1rem',
height: '.5rem',
lineHeight: '.5rem',
background: '#000',
opacity: 0.85,
color: 'white',
};
var cssObject = options || defaultCssObject;
var temp = `<div class="v-toast">${text}</div>`;
var $div = $(temp);
$div.css(cssObject);
$('body').append($div);
clearTimeout(timer);
timer = setTimeout(() => {
$('.v-toast').remove();
timer = null;
}, delay);
}
}
}
vue父子組件生命周期執行順序問題
父子組件鈎子執行順序:
father created
father before mounted
son created
son before mount
son mounted
father mounted
所以,你在父組件 beforeMounted 中已初始化的值一定可以通過props下發到子組件
vue-router切換頁面滾動條位置問題
注意:scrollBehavior只有在history.pushState可用時才有效,而該方法僅兼容到IE10(移動端可放心使用)
const router = new VueRouter({
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) { // 通過meta屬性精細控制
from.meta.savedPosition = document.body.scrollTop;
}
return { x: 0, y: to.meta.savedPosition || 0 }
}
}
})
vue庫的引入問題
vue提供眾多版本庫文件,在使用*.vue單文件組件的開發模式時,只需引入vue.runtime.min.js即可,該庫體積最小,不過要注意初始化vue實例時,不能用template選項,而應該換為render
new Vue({
el: '#app',
router,
render: h => h(App)
})
// webpack對應修改:
alias: {
'vue$': 'vue/dist/vue.runtime.min.js'
}
vuex組件中訪問state報錯
TypeError: Cannot read property ‘state’ of undefined”
在組件中使用this.$store.state.test訪問state的屬性報錯,是因為store的實例並未注入到所有的子組件,需修改main.js
new Vue({
el: '#app',
store, //將store注入到子組件
router,
components: { App },
template: '<App/>'
})