vue-cli搭建項目
- 確保安裝了node與npm
- 再目標文件夾下打開終端
- 執行cnpm i vue-cli -g全局安裝
以上安裝完成后下次搭建不需再寫
- 運行vue init webpack vue_mall(項目名稱)
- 進入項目文件夾下執行cnpm install安裝package.json的依賴文件
- 運行項目執行npm run dev
關于每個組件內屬性的順序
多人開發時盡量保持每個組件 export default {} 內的方法順序一致,方便查找對應的方法。
推薦: data(方法)、props(數組)、鉤子(方法)、watch(對象)、computed(對象)、components(對象)
組件
- 在組件里的template里寫上html(注意要有個根元素,一般為div)
- 在組件里的style里寫上css樣式(如果不是作用于全局的樣式,一般在style后面跟上scoped,但是要是加了反而改變了原有的樣式,就不要加)
注冊
- 全局注冊
Vue.component('my-component', { //在main.js中
// 選項
}) - 局部注冊
components: {
// <my-component> 將只在父組件模板中可用
'my-component': Child //es6語法:相同時可只寫一個
}
vue-router
導入vue-router(main.js)
import vueRouter from 'vue-router'-
html寫代碼
- 寫觸發鏈接的標簽(按需,沒有可不寫)
<router-link to="/newslist">新聞列表</router-link>
- 寫觸發鏈接的標簽(按需,沒有可不寫)
路由的占位符
<router-view></router-view>-
javascript中寫代碼
- 定義組件【不要注冊,下面設置路由規則的時候,會自動把我們的組件注冊】
在component里新建一個組件xxx.vue - 創建路由對象,設置路由規則(自動幫我們把組件注冊)(router/index.js)
在router/index.js中創建router,并設置routes
const router = new vueRouter({
routes: [{
path: '/site',
component: Layout
}]
}
3.把我們上一步創建的路由對象,注入到根實例,這樣我們整個應用就擁有了路由的功能(main.js)
new Vue({
el: '#app',
router,
render: h => h(App)
})
- 定義組件【不要注冊,下面設置路由規則的時候,會自動把我們的組件注冊】
$router&$route
-
相同點:
- 都是屬于vue-router里面的
- 必須要在集成vue-router的時候,使用Vue.use(VueRouter),才會在vue原型上面綁定$route、$router這兩個屬性
-
不同點:
- $router是在編程式導航的時候,使用到它,它里面有兩個方法 $router.push、$router.go
- $route 用來獲取路徑中的參數,$route.params.xxx,還可以通過 $route.query.xxx來獲取路徑中的參數 ,在監控路徑變化的時候,使用到它
axios(獲取網絡請求)
- 導入axios(main.js)
import axios from 'axios'
-
使用axios(main.js)
Vue.prototype.$axios = axios
axios.defaults.baseURL = 'http://39.108.135.214:8899/'(方便起見可以設置根路由)
axios.defaults.withCredentials = true (在跨域的時候,允許訪問服務器時帶上cookies)
-
1發送get請求
3.1.1 在發起請求組件的methods里添加函數getGoodsGroup(){
const url = 'site/goods/getgoodsgroup/123'
this.$axios.get(url).then((response)=>{
this.goodsGroup = response.data.message
})
}
3.1.2 在組件加載前運行該請求函數
created(){
this.getGoodsGroup()
}
- 2發送post請求
與get類似,不過請求參數的設置有所區別
-
有兩個格式可選,具體選哪種看后臺設置的contentType而定,一般后臺兩個格式都可以接收
- this.$axios.post(url,{username:'zhangsan',password:123}).then(response=>{})
- this.$axios.post(url,"username=zhangsan&password=123").then(response=>{})
導入文件
-
導入樣式
- 全局導入(main.js)
import './statics/site/css/style.css' - 局部導入
@import './statics/site/css/style.css'
- 全局導入(main.js)
-
導入插件
- 安裝
cnpm i xxx -S
- 安裝
- 導入
import vueRouter from 'vue-router' - 全局使用(如果不需要全局使用,則這步可以省略)
2.1 基于vue
Vue.use(vueRouter)
2.2 不基于vue
Vue.prototype.$axios = axios
- 導入jQuery
1.導入jQuery- 安裝
cnpm i jquery -S - 在build/webpack.base.conf.js里加入
var webpack = require("webpack") - 在module.exports的最后加入:
new webpack.optimize.CommonsChunkPlugin('common.js'),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
}) - 在main.js中引入
import $ from 'jquery' - 最后一定要重新npm run dev
2.導入jQuery插件 - 確保安裝了jquery
- 在script中引入插件js
import '~/site/js/jqueryplugins/jqimgzoom/js/magnifier.js' - 在style中引入插件css
@import '../../../static/site/js/jqueryplugins/jqimgzoom/css/magnifier.css'; - 在script的mounted生命周期鉤子中初始化,一般要給其增加延時,以防數據沒有請求回來
setTimeout(() => {
$(function () {
$('#magnifier1').imgzoon({
magnifier: '#magnifier1'
});
});
}, 200)
- 安裝
關于router-link
- router-link會自動的把該元素變成a標簽
- 添加了to屬性后,不需要原來的href屬性
- to屬性動態獲取其他值時前面要加冒號(:)
- to屬性動態拼接時外面的雙引號要寫在里面所有字符串的外面,固定不變的值外面加上單引號以加號連接外部變量
過濾器
使用:加在雙花括號插值表達式中,以管道符號'|'指示:
{{message | dateFmt}}定義:在局部組件的選項中定義局部過濾器,或者在創建 Vue 實例之前全局定義過濾器
-
內部的函數可接受多個參數,第一個參數為上面message的值
filters: { //局部過濾器(xxx.vue)
dateFmt: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}Vue.filter('dateFmt',(value,fmt='YYYY-MM-DD')=>{ //全局過濾器(main.js)
return moment(value).format(fmt)
})new Vue({
// ...
})
關于已定義的數據在模板與vue實例中調用形式
- 數據在模板中調用直接寫屬性名
- 數據在vue實例中調用要在前面寫上this,由vue實例調用
關于動畫
- 在最外面的div內定義transition組件,里面放要動畫的元素
- 給要動畫的元素設置css過渡樣式:
transition: all .5s - 給要動畫的元素設置顯示隱藏:
v-show="isShowPic" - 由另一個button設置觸發事件,改變isShowPic
- 給transition組件添加動畫鉤子:
進入:@before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"
移出:@before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave" - 在methods中定義各個動畫鉤子
beforeEnter: function (el) {
el.style = 'transform:translateX(200px)'
},
enter: function (el, done) {
el.offsetWidth
el.style.transform = 'translateX(0px)'
done()
},
afterEnter: function (el) {
this.isShow = false
},
beforeLeave: function (el) {
el.style = 'transform:translateX(0px)'
},
leave: function (el, done) {
el.offsetWidth
el.style = 'transform:translateX(200px)'
el.addEventListener('transitionend', done)
},
afterLeave: function (el) {
this.isShow = false
} - 注意:
- el指的是該動畫元素
- 在before里寫動畫的起始狀態或位置、enter或leave里寫動畫的結束狀態或位置
- after里寫動畫的回調函數(如消失隱藏),在enter或leave里調用
- 關于done在進入時可直接調用,不過在離開時需要在過渡完成事件里調用
- 在enter或著leave中要添加el.offsetWidth刷新動畫幀
關于ref
在dom元素中定義ref屬性,則可在vue示例中通過this.refs獲取所有有ref屬性的dom元素
ref獲取不到其父組件所定義ref的dom元素,可用id獲取
關于監聽路由跳轉
在watch屬性里添加監聽對象$route:對應一個函數,當路由的值發生變化時,重新渲染頁面
watch: {
// 監控路由變化
$route: function (val) {
// 刷新商品詳情和評論數據
this.getGoodsinfoData()
this.getCommentData()
}
},
關于vuex
-
導入vuex,并聲明全局使用(main.js)
import Vuex from 'vuex'
Vue.use(Vuex) -
創建store對象(main.js)
const store = new Vuex.Store({
state: {
count:1
},
getters:{
//獲取state數據
getCount(state){
return state.count
}
}, //對倉庫的增刪改
mutations: {
addGoods(state,goodsObj){
state.count = addLocal(goodsObj)
}
}
}) 注入根實例(main.js)
new Vue({
el: '#app',
router,
store, //<--見此處
components: { App },
template: '<App/>'
})-
一般vuex會結合localStorage使用
4.1 新建一個common文件夾,一般與main.js同級,在里面創建一個localTool.js,專門用來寫對本地存儲的增刪改查操作,并暴露出去
4.2 查找
const getLocal = () => {
return JSON.parse(localStorage.getItem('goods') || '{}')
}- 3 增加
export const addLocal = (goodsObj) => {
const localGoods = getLocal()
if (localGoods[goodsObj.id]) {
localGoods[goodsObj.id] += goodsObj.count
} else {
localGoods[goodsObj.id] = goodsObj.count
}
localStorage.setItem('goods', JSON.stringify(localGoods))
return getTotal() //調用其他函數返回一個值
}
- 3 增加
在創建store對象前引入localTool.js
import {addLocal} from './common/localTool'在store對象中添加mutations對象的方法,
示例方法是把值存入本地,調用localTool.js的addLocal方法,并同步state的count
mutations: {
addGoods(state,goodsObj){
state.count = addLocal(goodsObj)
}}-
使用
- 使用mutations對象里的方法
在調用mutation其中方法的組件中,使用:
this.$store.commit('addGoods',goodsObj)
【第一個參數是mutations的方法名,其他可選,是調用過程中會使用的參數】 - 使用getters獲取state(數據)
在使用vuex狀態(即數據)的組件中,直接使用:
this.$store.getters.getCount
- 使用mutations對象里的方法
父子組件傳值
一般情況
- 在父組件中集成子組件
1.1 創建子組件
新建一個vue組件,如:inputnumber.vue
1.2 在父組件中導入子組件
import inputnumber from '../subcomponents/inputnumber'
1.3 在父組件的components中注冊子組件
components: { inputnumber }
1.4 直接在父組件的template(模板)中,像自定義標簽的形式使用
<inputnumber></inputnumber> - 父組件傳值給子組件【通過props】
2.1 接收方 (inputnumber.vue) :子組件
子組件要顯式地用 props 選項聲明它預期的數據:
props: ['initCount']- 2 發送方 (shopcart.vue) :父組件
在使用子組件的地方,即在子組件的標簽中,通過 屬性名稱=值 的方式傳值,可動態傳值
<inputnumber :initCount="item.buycount"></inputnumber>
- 2 發送方 (shopcart.vue) :父組件
- 子組件 把更改之后的值 傳回給父組件 【通過自定義事件】
3.1 接收方 (shopcart.vue) :父組件
3.1.1 父組件可以在使用子組件的地方直接用 v-on 來監聽子組件觸發的事件
@countChange="getChangedCount"
3.1.2 父組件在methods中定義觸發自定義事件后的方法
getChangedCount(changedGoods){} //changedGoods為傳回的值
3.2 發送方 (inputnumber.vue):子組件
通過觸發事件傳值
this.$emit('countChange',{count:this.count}) //值可以是任何類型
element-ui計數器的父子傳值
-
集成組件,設置他的最大和最小值
<template>
<el-input-number size="mini" :min="1" :max="10" v-model="num7"></el-input-number>
</template>
<script>
export default {
data() {
return {
num7: 1
}}};
</script> 把要傳給計數器的值放在v-model中,即修改v-model的值
-
把計數器增減后的值傳回來
- 設置change事件(標簽),其中第一個參數是另外傳給計數器的值,最后一個參數是計數器完成操作后返回的結果
@change="changeCount(item.id, $event)" - 設置change事件(methods)
changeCount(value, event){
const goodsObj = {
goodsid:value,
count:event
};
console.log(goodsObj);
}
- 設置change事件(標簽),其中第一個參數是另外傳給計數器的值,最后一個參數是計數器完成操作后返回的結果
非父子組件傳值
- 只有在父組件中通過components: { inputnumber }注冊的才能稱為父子組件,在template通過router-view占位的不算父子組件
-
新建一個common.js文件,定義一個組件bus作為非父子組件的中轉站
import Vue from 'vue'
export const bus = new Vue() 兩個組件分別引入bus
import {bus} from '@/common/common'
- 在發送組件的methods中觸發事件
bus.$emit(ISLOGIN,true)
-
在接收組件的created中監聽事件
created(){
bus.$on(ISLOGIN,(logined)=>{
this.isLogin = logined
})
},
點擊刪除,刪除某項數據
- 數據在后臺
發起刪除數據請求,獲得數據后,重新渲染頁面 - 數據在本地(利用vue的數據驅動,修改data中的值,頁面也會相應的變化)
2.1 利用vuex刪除本地數據
2.2 在之前獲得的渲染頁面的數組中,直接刪除對應索引的數據
登陸驗證
需要登陸驗證的組件,在設置該路由規則時,添加元數據(router)
meta:{needLogin:true}-
利用導航守衛,給需要登陸驗證的組件(即meta有needLogin),發送請求給后臺,判斷是否登陸(router)
- 注意:router.beforeEach里一定要有next(),否則路由根本不會跳轉
router.beforeEach((to,from,next)=>{
if(to.meta.needLogin){
const url = 'site/account/islogin'
axios.get(url).then(res=>{
if(res.data.code === 'nologin'){
router.push({ name:'login'}) // 去登陸頁
}else{
next() // 正常路由跳轉
}
})
}else{
next()
}}) -
在router.beforeEach中將要跳轉的路徑保存到本地(router)
if(to.path!='/site/login'){
localStorage.setItem('lastVisited',to.path)
} 在登陸頁中登陸成功后,跳轉到本地保存的路徑(login.vue)
this.$router.push({path:localStorage.getItem('lastVisited')})發送請求時默認帶上cookie(main.js)
axios.defaults.withCredentials = true
返回上一頁
-
直接返回上一頁
this.$router.go(-1)
經過登陸驗證后,返回他本該去的頁面
2.1 在路由守衛中本地保存要去的頁面的路徑,注意要排除掉/login
if (to.path != '/site/login') {
localStorage.setItem('lastVisited', to.path);
}
2.2 登陸成功時,設置跳到本地保存的路徑中
this.$router.push({path:localStorage.getItem('lastVisited')})
Vue組件的生命周期
基本概念
Vue:
beforeCreate(組件創建之前) ---> created(組件已經創建出來了)
---> beforeMount(組件的dom元素被渲染出來之前) ---> mounted(dom元素已經渲染出來了) ---> 【模型數據發生了更改】beforeUpdate(視圖重新渲染之前) ---> updated(視圖已經重新渲染完畢) ---> beforeDestory(組件銷毀之前) ---> destoryed(組件銷毀了)
注意點:
1、Vue的一系列生命周期鉤子,都是Vue框架提供者,我們開發者,只需要
實現,那么我們Vue框架底層就會在恰當的時機,自動調用他們
2、每個組件中都有這些生命周期鉤子
應用場景:
1、created
發送網絡請求,獲取數據
2、mounted
等視圖渲染完成,然后拿著dom進行操作,有時候可能拿不到dom元素,或者有些效果出不來,可以嘗試加200ms的延時
如:使用jQuery插件
3、beforeUpdate & update
數據模型發生了更改,會調用,它會重新渲染組件
4、beforeDestory & destory
beforeDestory 記錄未提交的數據
created 將本地的數據,自動填充上
beforeDestory:記錄上次滾動到那個地方了
created:自動滾動到你上次看得那個位置
使用vue-cli打包
使用npm run build
修改config/index.js里module.exports的assetsPublicPath改為:
assetsPublicPath: './'修改build/utils.js里generateLoaders的publicPath改為:
publicPath: '../../'-
如果引入了jQuery,則在webpack.prod.conf.js里也要聲明全局使用,在module.exports的最后加入:
new webpack.optimize.CommonsChunkPlugin('common.js'),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
}) 若想刪掉自動生成的map文件:
修改config/index.js里module.exports的productionSourceMap改為productionSourceMap: false,
vue-cli優化
刪掉.map
- .map文件只是幫助我們調試用的,正式上線時可以去掉這個文件
- 修改config/index.js里module.exports的productionSourceMap改為productionSourceMap: false
element-ui和iview按需導入
element-ui
- 安裝 babel-plugin-component:
cnpm install babel-plugin-component -D
-
將 .babelrc 修改為:
{
"presets": [
["env", { "modules": false }]
],
"plugins": [["component", {
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]
} -
在 main.js 中引入部分組件
- 如只引入 Button , Select 和 Message
import { Button, Select, Message} from 'element-ui'
Vue.use(Button) //順便會導入Button的css,不需另外導入
Vue.use(Select)
Vue.prototype.$message = Message //有部分組件不能直接通過use聲明全局使用
- 如只引入 Button , Select 和 Message
iview
- 安裝 babel-plugin-import:
cnpm install babel-plugin-import -D
-
在 .babelrc 中配置:
{
"plugins": [["import", {
"libraryName": "iview",
"libraryDirectory": "src/components"
}]]
} -
按需引入組件
import { Button, Table } from 'iview';
Vue.component('Button', Button);
Vue.component('Table', Table); 導入樣式
import 'iview/dist/styles/iview.css';
路由懶加載
- 建議:在剛開始時直接用這種方式引入路由文件
- 安裝babel-plugin-syntax-dynamic-import
cnpm i babel-plugin-syntax-dynamic-import -D
-
在 .babelrc 中配置:
{
"plugins": ["syntax-dynamic-import"]
} -
修改路由中引入文件的方式,把
//(原來)import Layout from '@/components/Layout'
const Layout = () => import('@/components/Layout')
CDN引入
- 將jquery、moment等大資源的文件通過cdn的方式引入
- 常見cdn:bootcdn
- 實現:
- 引入資源
<script src="https://cdn.bootcss.com/vue/2.4.4/vue.min.js"></script> - 公開供全局使用,修改bulid文件夾下的webpack.base.conf.js文件
- 這里小寫的vue和vue-router是我們引入資源時對應的名字,冒號后面大寫的名字是庫的主人所暴露出來的全局方法名,當然這兩個名字可以一樣
module.exports = {
entry: {
app: './src/main.js'
},
externals:{
'BMap': 'BMap',
'vue': 'Vue',
'vue-router': 'VueRouter'
}
- 這里小寫的vue和vue-router是我們引入資源時對應的名字,冒號后面大寫的名字是庫的主人所暴露出來的全局方法名,當然這兩個名字可以一樣
- 將項目中引用對應資源的地方將原先的引入方式去掉
// import Vue from 'vue'
// import VueRouter from 'vue-router'
- 引入資源
BUG
關于渲染值undefined
- 請求是異步費時操作,在渲染時數據還沒有返回回 來,此時就會報undefined錯誤
- 雖然之后數據返回后會再次渲染,但剛開始報的錯不會消失
- v-for不需擔心undefined的問題
- 直接拿數據渲染時可以在其父盒子添加v-if=“渲染數組/對象”,則在沒有返回數據時不會渲染頁面
關于vue設置屬性時,number類型的值設置無效
- vue中屬性的值為number類型不能直接寫label:1,要寫:label:“1”
關于css樣式與原來不一致
- 設置css樣式的style加了scoped導致,刪掉就可還原
在路由中設置了site/login,但是當a標簽的href設置為‘/site/login’時不會跳轉到對應頁面
- vue中的無刷新跳轉是通過設置url的哈希值(即#)實現的,默認的會在url的末尾添加#,即若href設置為‘#/site/login’,則可以正常跳轉
- 不過vue還是建議我們用router-link來實現跳轉,使用時不用擔心#的問題
<router-link to="/site/login"></router-link>
網站本來能正常顯示,設置了路由守衛后連首頁都打不開了
- 在設置路由守衛時沒有調用next()
由當前頁面通過路由跳轉顯示不同數據時,jquery插件在mounted鉤子中初始化后只能顯示第一次時的數據
jQuery是事件驅動,vue是數據驅動
同頁面的路由跳轉并沒有銷毀并重新打開新的組件,在mounted中初始化的插件,在路由跳轉后并不執行初始化函數,所以不能顯示
可以把初始化函數放到加載頁面數據的請求回來后,這樣每次重新渲染頁面時就會重新加載初始化函數
一般把jQuery放入axios中
其他
關于foreach和for in
-
foreach一般用來遍歷數組:
myArry.forEach((value,index,arr)=>{
console.log(value);
}); -
for in一般用來遍歷對象:
for(var value in myArry){
console.log(value)
}
關于localStorage
- localStorage存的起始都是字符串,所以不能對其中的某個鍵值對進行增刪改查操作
- 數組化某個localStorage
var contrastdata = JSON.parse(localStorage.getItem('contrastdata')) - 對數組進行增刪改操作
contrastdata[a] = b
delete contrastdata[a]
contrastdata[a] = c - 將操作后的數組存入該localStorage
localStorage.setItem(JSON.stringify(contrastdata))
把數組中對象的某個鍵對應的值取出來
- 如:[{id:1,age:2},{id:2,age:4},{id:3,age:5}]
- 創建一個新數組
const tempArr = [] - 遍歷這個數組,把對象中某個鍵對應的值放入該數組中
this.shopcartgoods.forEach(item=>{
tempArr.push(item.id)
}) - 轉成字符串
tempArr.join(',')
數組與字符串互轉
- 數組轉字符串
var a, b,c;
a = new Array(a,b,c,d,e);
b = a.join('-'); //a-b-c-d-e 使用-拼接數組元素
c = a.join(''); //abcde - 字符串轉數組
var str = 'ab+c+de';
var a = str.split('+'); // [ab, c, de]
var b = str.split(''); //[a, b, +, c, +, d, e]