項(xiàng)目準(zhǔn)備
- 在碼云新建倉(cāng)庫(kù)travel
- 克隆到本地
- 在本地倉(cāng)庫(kù)所在目錄執(zhí)行
vue init webpack travel
選擇y
表示繼續(xù)
樣式重置
index.html
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
reset.css
- 在
src/assets/styles
目錄下存放reset.css
@charset "utf-8";
html {
background-color: #fff;
color: #000;
font-size: 12px;
}
body,
ul,
ol,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
figure,
form,
fieldset,
legend,
input,
textarea,
button,
p,
blockquote,
th,
td,
pre,
xmp {
margin: 0;
padding: 0;
}
body,
input,
textarea,
button,
select,
pre,
xmp,
tt,
code,
kbd,
samp {
line-height: 1.5;
font-family: tahoma, arial, 'Hiragino Sans GB', simsun, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6,
small,
big,
input,
textarea,
button,
select {
font-size: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: tahoma, arial, 'Hiragino Sans GB', '微軟雅黑', simsun, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6,
b,
strong {
font-weight: normal;
}
address,
cite,
dfn,
em,
i,
optgroup,
var {
font-style: normal;
}
table {
border-collapse: collapse;
border-spacing: 0;
text-align: left;
}
caption,
th {
text-align: inherit;
}
ul,
ol,
menu {
list-style: none;
}
fieldset,
img {
border: 0;
}
img,
object,
input,
textarea,
button,
select {
vertical-align: middle;
}
article,
aside,
footer,
header,
section,
nav,
figure,
figcaption,
hgroup,
details,
menu {
display: block;
}
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '\0020';
}
textarea {
overflow: auto;
resize: vertical;
}
input,
textarea,
button,
select,
a {
outline: 0 none;
border: none;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}
mark {
background-color: transparent;
}
a,
ins,
s,
u,
del {
text-decoration: none;
}
sup,
sub {
vertical-align: baseline;
}
html {
overflow-x: hidden;
height: 100%;
font-size: 50px;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: Arial, 'Microsoft Yahei', 'Helvetica Neue', Helvetica, sans-serif;
color: #333;
font-size: 0.28em;
line-height: 1;
-webkit-text-size-adjust: none;
}
hr {
height: 0.02rem;
margin: 0.1rem 0;
border: medium none;
border-top: 0.02rem solid #cacaca;
}
a {
color: #25a4bb;
text-decoration: none;
}
- 在mian.js文件引入
import './assets/styles/reset.css'
解決移動(dòng)端1像素邊框問(wèn)題
- 在
src/assets/styles
目錄下存放border.css - 在mian.js文件引入
import './assets/styles/border.css'
解決移動(dòng)端300ms點(diǎn)擊延遲
安裝fastclick
npm install fastclick --save
main.js
import fastClick from 'fastclick'
fastClick.attach(document.body)
字體圖標(biāo)
在iconfont新建項(xiàng)目
在項(xiàng)目中使用sass
npm install sass-loader node-sass --save-dev
注意sass-loader版本過(guò)高可能會(huì)報(bào)錯(cuò)
頁(yè)面組件化
將一個(gè)頁(yè)面拆分成多個(gè)組件
src/pages/home
目錄下新建components
目錄,然后新建Header.vue
引入
src/pages/home/home.vue
<template>
<div><home-header></home-header></div>
</template>
<script>
import HomeHeader from './components/Header'
export default {
name: 'Home',
components: {
//es6中鍵值相同可以省略值
HomeHeader
}
}
</script>
<style></style>
Vue自動(dòng)完成HomeHeader和小寫(xiě)的<home-header>的關(guān)聯(lián)
頁(yè)面元素高度問(wèn)題
由于移動(dòng)端一般使用雙倍像素肤晓,如果指定元素高度10px这刷,實(shí)際顯示為20px司草,所以實(shí)際指定高度應(yīng)為設(shè)計(jì)圖紙中的一半晴音。在實(shí)際開(kāi)發(fā)中哎甲,一般使用rem作為單位孝冒,我們可以指定html的font-size為50px洋幻,如果設(shè)計(jì)圖上某個(gè)元素高度65px,轉(zhuǎn)化為rem值為0.65rem(css中元素高度應(yīng)為設(shè)計(jì)圖上該元素高度的一半)
引入字體圖標(biāo)
在styles目錄新建iconfont
,把從Iconfont下載的字體圖標(biāo)放進(jìn)去
iconfont.css
在styles目錄下队秩,需要修改字體路徑笑旺,如
src: url('./iconfont/iconfont.eot?t=1584759696965');
main.js
中
import './assets/styles/iconfont.css'
使用圖標(biāo)
<span class="iconfont iconfanhui"></span>
使用scss變量
在src/assets/styles
目錄下新建_variables.scss
,存放變量
// demo
$bgColor: #00bcd4;
在項(xiàng)目中引入
src/pages/home/components/Header.vue
<style lang="scss" scoped>
@import '~@/assets/styles/_variables';
...
</style>
注意@
表示src目錄馍资,前面的~
必須加上才不會(huì)報(bào)錯(cuò)
給路徑添加別名
給路徑添加別名好處是減少路徑長(zhǎng)度
build/webpack.base.conf.js
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
alias用于添加別名筒主,如果要讓styles指向assets/styles,只需添加
styles: resolve('src/assets/styles')
main.js導(dǎo)入樣式文件可以寫(xiě)成
main.js
import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'
//import './assets/styles/iconfont.css'
組件中導(dǎo)入scss文件寫(xiě)成
src/pages/home/components/Header.vue
@import '~styles/_variables';
重啟服務(wù)器后生效
新建項(xiàng)目分支
在實(shí)際項(xiàng)目開(kāi)發(fā)過(guò)程中鸟蟹,每開(kāi)發(fā)一個(gè)新功能都會(huì)創(chuàng)建一個(gè)新的分支物舒,功能開(kāi)發(fā)完成之后再合并到主分支
//提交主分支
git add .
git commit -m 'header finished'
git push
//創(chuàng)建新分支
git checkout -b index-swiper
使用輪播插件
npm install swiper vue-awesome-swiper --save
main.js
import VueAwesomeSwiper from 'vue-awesome-swiper'
// import style
import 'swiper/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default options with global component } */)
解決自動(dòng)輪播不生效問(wèn)題
swiperOptions: {
observer: true, //修改swiper自己或子元素時(shí),自動(dòng)初始化swiper
observeParents: true, //修改swiper的父元素時(shí)戏锹,自動(dòng)初始化swiper
loop: true,
autoplay: {
delay: 3000
},
pagination: {
el: '.pagination-home'
}
// Some Swiper option/callback...
},
解決頁(yè)面抖動(dòng)問(wèn)題
加載頁(yè)面時(shí)冠胯,圖片在文字之后加載,會(huì)造成文字剛開(kāi)始占用圖片的位置锦针,之后又被圖片擠開(kāi)荠察。
解決辦法:圖片外面加一層div
<div class="wrapper">
...
</div>
.wrapper {
overflow: hidden;
width: 100%;
height: 0;
//padding 百分比相對(duì)于父元素的寬度置蜀,這里是屏幕寬度,31.25%是圖片實(shí)際高度/圖片寬度
padding-bottom: 31.25%;
.swiper-img {
width: 100%;
}
}
上傳分支代碼
git add .
git commit -m 'swiper finished'
//第一次上傳分支
git push -u origin index-swiper
//git push
合并到主分支
//切換到主分支
git checkout master
//把線上分支合并到本地分支
git merge origin/index-swiper
//提交master分支
git push
文字超出部分顯示省略號(hào)
assets/styles
文件夾下新建_mixin.scss
悉盆,該文件主要寫(xiě)重復(fù)使用的樣式
@mixin ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
使用
@import '~styles/mixin';
.icon-desc {
@include ellipsis;
}
注意:1. 文件名以下劃線開(kāi)頭盯荤,但import時(shí)不帶下劃線。
- 如果是flex布局焕盟,且元素
flex:1
秋秤,加上min-width: 0;
確保該子元素不超過(guò)外層容器
.item-info {
flex: 1;
padding: 0.1rem;
min-width: 0;
}
發(fā)送ajax請(qǐng)求
npm install axios --save
一個(gè)頁(yè)面可能有多個(gè)組件組成,如果每個(gè)組件都需要獲取服務(wù)器的數(shù)據(jù)脚翘,這樣效率比較低下灼卢,推薦的做法是在父組件發(fā)送請(qǐng)求。
Home.vue
import axios from 'axios'
created() {
this.getHomeInfo()
},
methods: {
//模擬請(qǐng)求數(shù)據(jù)
getHomeInfo() {
axios.get('/api/index.json').then(this.getHomeInfoSucc)
},
getHomeInfoSucc(res) {
console.log(res)
}
}
mock
在沒(méi)有后端支持情況下来农,需要axios模擬請(qǐng)求數(shù)據(jù)鞋真,這就要用到mock
在static文件夾下新建mock目錄,所有請(qǐng)求的數(shù)據(jù)放在該文件夾下沃于。
static/mock/index.json
{
"status": "ok"
}
修改Home.vue
getHomeInfo() {
axios.get('/static/mock/index.json').then(this.getHomeInfoSucc)
},
static目錄下的文件可以在瀏覽器通過(guò)路徑直接訪問(wèn)涩咖,一般我們不希望其發(fā)布到線上,可以為其添加gitignore
.gitignore
static/mock
配置轉(zhuǎn)發(fā)
需求:經(jīng)過(guò)上面的步驟繁莹,我們已經(jīng)可以通過(guò)獲得數(shù)據(jù)檩互,但也引入另一個(gè)問(wèn)題,即服務(wù)器端api地址和模擬請(qǐng)求的地址不一致咨演,項(xiàng)目上線時(shí)需要考慮api地址變更闸昨,webpack提供了解決這個(gè)問(wèn)題的辦法。
config/index.js
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
//修改部分
proxyTable: {
'/api': {
target: 'http://localhost:8080',
pathRewrite: {
// 請(qǐng)求以/api開(kāi)頭的轉(zhuǎn)發(fā)到/static/mock
'^/api': '/static/mock'
}
}
},
...
}
proxyTable
下配置路由轉(zhuǎn)發(fā)
現(xiàn)在已經(jīng)可以通過(guò)/api/
訪問(wèn)接口了
getHomeInfo() {
axios.get('/api/index.json').then(this.getHomeInfoSucc)
}
解決組件渲染沒(méi)有數(shù)據(jù)問(wèn)題
<swiper :options="swiperOptions" v-if="swiperList.length">
<swiper-slide v-for="item of swiperList" :key="item.id"><img class="swiper-img" :src="item.imgUrl" alt="" /> </swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
swiper
創(chuàng)建時(shí)數(shù)據(jù)還沒(méi)有生成雪标,這個(gè)時(shí)候需要加上v-if="swiperList.length"
表示獲得數(shù)據(jù)之后才會(huì)渲染這個(gè)組件
改進(jìn):雖然這樣可以解決問(wèn)題零院,但我們建議模板中盡可能減少邏輯代碼的出現(xiàn)溉跃。使用computed來(lái)解決這個(gè)問(wèn)題
computed: {
showSwiper() {
return this.swiperList.length
}
}
修改tempate
<swiper :options="swiperOptions" v-if="showSwiper">
...
</swiper>
優(yōu)化滑動(dòng)
npm install better-scroll --save
使用該組件需要dom結(jié)構(gòu)滿足一定條件
<div class="list" ref="wrapper">
<div>
<div></div>
<div></div>
<div></div>
</div>
</div>
import BScroll from 'better-scroll'
mounted() {
this.scroll = new BScroll(this.$refs.wrapper)
}
實(shí)現(xiàn)父子組件聯(lián)動(dòng)
Alphabet.vue
(子組件)
<template>
<ul class="list">
<li
class="item"
v-for="item of letters"
:key="item"
@click="handleLetterClick"
@touchstart.stop.prevent="handleTouchStart"
@touchmove.stop.prevent="handleTouchMove"
@touchend.stop.prevent="handleTouchEnd"
:ref="item"
>
{{ item }}
</li>
</ul>
</template>
-
@click="handleLetterClick"
監(jiān)聽(tīng)點(diǎn)擊了哪個(gè)letter村刨,通知父組件letter改變,父組件再通知city-list組件letter改變(借助父組件實(shí)現(xiàn)兄弟組件通信) - touchstart撰茎、touchmove嵌牺、touchend監(jiān)聽(tīng)觸摸事件
updated() {
// update在獲取數(shù)據(jù)后被執(zhí)行
// 獲取'A'元素距離其父元素上邊緣的距離
this.startY = this.$refs['A'][0].offsetTop
},
methods: {
handleLetterClick(e) {
this.$emit('change', e.target.innerText)
},
handleTouchStart() {
this.touchStatus = true
},
handleTouchMove(e) {
if (this.touchStatus) {
// e.touches[0].clientY表示鼠標(biāo)指針距屏幕頂部的距離
// 79為導(dǎo)航欄的高度
const touchY = e.touches[0].clientY - 79
// 鼠標(biāo)經(jīng)過(guò)字符下標(biāo)
const index = Math.floor((touchY - this.startY) / 20)
if (index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
}
},
handleTouchEnd() {
this.touchStatus = false
}
}
City.vue
(父組件)
<city-list :cities="cities" :hotCities="hotCities" :letter="letter"></city-list>
<city-alphabet :cities="cities" @change="handleLetterChange"></city-alphabet>
handleLetterChange(letter) {
this.letter = letter
}
List.vue
(子組件)
watch: {
letter(newVal, oldVal) {
if (newVal) {
const element = this.$refs[newVal][0]
this.scroll.scrollToElement(element)
}
}
}
監(jiān)聽(tīng)父組件傳過(guò)來(lái)的letter的變化,滾動(dòng)到屏幕相應(yīng)位置
優(yōu)化:手指移動(dòng)的速度很快龄糊,可以通過(guò)設(shè)置定時(shí)函數(shù)延遲響應(yīng)滑動(dòng)事件
搜索
Search.vue
<template>
<div>
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="輸入城市名或拼音" />
</div>
<!-- keyword有值時(shí)顯示 -->
<div class="search-content" ref="search" v-show="keyword">
<ul>
<li class="serach-item" v-for="(item, index) of list" :key="index">{{ item.name }}</li>
<!-- 沒(méi)有查詢結(jié)果時(shí)顯示 -->
<li class="serach-item" v-show="hasNoData">沒(méi)有找到匹配數(shù)據(jù)</li>
</ul>
</div>
</div>
</template>
computed: {
//判斷是否有數(shù)據(jù)
hasNoData() {
return !this.list.length
}
},
watch: {
keyword(newVal, oldVal) {
if (this.timer) {
clearTimeout(this.timer)
}
// 關(guān)鍵字為空逆粹,關(guān)閉搜索結(jié)果頁(yè)
if (!newVal) {
this.list = []
return
}
// 監(jiān)聽(tīng)關(guān)鍵字變化,顯示查詢
const result = []
for (let i in this.cities) {
this.cities[i].forEach(value => {
if (value.spell.indexOf(newVal) > -1 || value.name.indexOf(newVal) > -1) {
result.push(value)
}
})
}
this.list = result
// this.timer = setTimeout(() => {
// const result = []
// for (let i in this.cities) {
// this.cities[i].forEach(value => {
// if (value.spell.indexOf(newVal) > -1 || value.name.indexOf(newVal) > -1) {
// result.push(value)
// }
// })
// }
// this.list = result
// }, 100)
}
},
沒(méi)有共同父組件的組件通信Vuex
npm install vuex --save
main.js
//store
//import會(huì)自動(dòng)尋找目錄下的index.js
import store from './store'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: '北京'
},
// actions調(diào)用mutation改變數(shù)據(jù)
actions: {
changeCity(ctx, city) {
// 調(diào)用mutations中changeCity的方法
ctx.commit('changeCity', city)
}
},
mutations: {
changeCity(state, city) {
state.city = city
}
}
})
獲取store中的數(shù)據(jù)
<div class="header-right">
{{ this.$store.state.city }}
<span class="iconfont iconjiantouxia arrow-icon"></span>
</div>
觸發(fā)改變store中的數(shù)據(jù)的函數(shù)
<div class="button-wrapper" v-for="item of hotCities" :key="item.id" @click="handleCityClick(item.name)">
handleCityClick(city) {
// 派發(fā)changeCity的Action
this.$store.dispatch('changeCity', city)
// 不經(jīng)過(guò)action的寫(xiě)法
this.$store.commit('changeCity', city)
}
頁(yè)面跳轉(zhuǎn)
- 鏈接式
<router-link to="/"></router-link>
- 編程式
this.$router.push('/')
router-link
<router-link :to="'/detail/' + item.id" tag="li" class="item" v-for="item of recommendList" :key="'recommend' + item.id">
...
</router-link>
<router-link>標(biāo)簽?zāi)J(rèn)情況下渲染為一個(gè)a標(biāo)簽炫惩,可以通過(guò)增加tag屬性指定希望渲染成的標(biāo)簽僻弹,如tag="li"
會(huì)最終渲染為li
標(biāo)簽
路由傳遞參數(shù)
router/index.js
export default new Router({
routes: [
...
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
]
})
Recommend.vue
<router-link :to="'/detail/' + item.id" tag="li" class="item" v-for="item of recommendList" :key="'recommend' + item.id">
...
</router-link>
全局組件
在src目錄下新建common目錄,用于存放全局公用組件
修改webpack.base.conf.js
增加common
的別名
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
vue$: 'vue/dist/vue.esm.js',
'@': resolve('src'),
styles: resolve('src/assets/styles'),
common: resolve('src/common')
}
},
樣式覆蓋
遇到組件不能覆蓋另一組件的樣式他嚷,就需要用樣式穿透蹋绽,在scss中可以用>>>
或者/deep/
.container >>> .swiper-container {
overflow: inherit;
}
props
props里的屬性的默認(rèn)值可以是函數(shù)
props: {
imgs: {
type: Array,
default() {
return ['a','b']
}
}
}
解決swiper插件顯示問(wèn)題
如圖芭毙,swiper不能顯示正確的下標(biāo),是因?yàn)殇秩具@個(gè)組件后其父元素是隱藏的卸耘,當(dāng)它顯示時(shí)就會(huì)出現(xiàn)這個(gè)問(wèn)題退敦,解決辦法是在讓swiper監(jiān)聽(tīng)父元素變化
data() {
return {
swiperOptions: {
pagination: {
el: '.swiper-pagination',
type: 'fraction'
},
// 監(jiān)聽(tīng)父元素變化
observeParents: true,
observer: true,
loop: true
// Some Swiper option/callback...
}
}
}
Vue點(diǎn)擊事件
我們要實(shí)現(xiàn)點(diǎn)擊黑色區(qū)域返回首頁(yè),而點(diǎn)擊圖片不做任何響應(yīng)
<div class="container" @click.self.prevent="handleGalleryClick">
...
</div>
@click.self.prevent
表示只監(jiān)聽(tīng)元素自身的點(diǎn)擊事件蚣抗,@click.prevent.self
剛好相反侈百,點(diǎn)擊自身無(wú)效,而點(diǎn)擊其子元素會(huì)被監(jiān)聽(tīng)到翰铡。
監(jiān)聽(tīng)屏幕滾動(dòng)事件
methods: {
handleScroll() {
// 滾動(dòng)的距離
const top = document.documentElement.scrollTop
// 距離頂部60開(kāi)始隱藏
if (top > 60) {
let opacity = top / 140
opacity = opacity > 1 ? 1 : opacity
this.showAbs = false
this.opacityStyle = {
opacity
}
} else {
this.showAbs = true
}
}
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
}
監(jiān)聽(tīng)屏幕事件必須在mounted()
方法中寫(xiě)window.addEventListener('scroll', this.handleScroll)
表示監(jiān)聽(tīng)屏幕滾動(dòng)钝域,并指定處理函數(shù)
對(duì)全局事件的解綁(重要)
在我們編寫(xiě)代碼的過(guò)程中,如果沒(méi)有對(duì)事件進(jìn)行及時(shí)的解綁两蟀,可能影響程序性能或造成錯(cuò)誤网梢。如下所示,我們?cè)谝粋€(gè)組件監(jiān)聽(tīng)window對(duì)象赂毯,當(dāng)這個(gè)組件銷毀后战虏,這個(gè)監(jiān)聽(tīng)仍在繼續(xù)。這顯然是不合理的党涕。
mounted() {
window.addEventListener('scroll', this.handleScroll)
}
改進(jìn):在組件銷毀前解綁事件
mounted() {
window.addEventListener('scroll', this.handleScroll)
},
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll)
}
使用遞歸組件
在組件內(nèi)部調(diào)用組件自身
使用遞歸組件必須滿足一個(gè)條件烦感,即組件需定義好了name屬性,根據(jù)name可以調(diào)用自身
<template>
<div>
<div class="item" v-for="(item, index) of list" :key="index">
<div class="item-title">
<span class="item-title-icon"></span>
{{ item.title }}
</div>
<div v-if="item.children">
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
</template>
export default {
name: 'DetailList',
props: {
list: {
type: Array
}
}
}
組件緩存
如果我們不希望某些頁(yè)面被緩存下來(lái)膛堤,而是每次進(jìn)入都重新獲取數(shù)據(jù)
<template>
<div id="app">
<keep-alive exclude="Detail">
<router-view />
</keep-alive>
</div>
</template>
exclude="Detail"
將name=Detail
的組件排除在緩存之外
小結(jié):組件name的三個(gè)用途
- 用于遞歸組件
- 用于Vue調(diào)試工具
- 用于keep-alive排除組件緩存
解決打開(kāi)頁(yè)面時(shí)頁(yè)面停留在底部
router/index.js
scrollBehavior
函數(shù)會(huì)在每次切換頁(yè)面時(shí)執(zhí)行
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
...
],
// 當(dāng)進(jìn)行頁(yè)面切換時(shí)手趣,x、y軸初始是0
scrollBehavior(to, form, savedPosition) {
return { x: 0, y: 0 }
}
})
添加動(dòng)畫(huà)
在common
目錄下新建fade目錄
common/fade/Fade.vue
<template>
<transition>
<slot></slot>
</transition>
</template>
<script>
export default {
name: 'Fade'
}
</script>
<style lang="scss" scoped>
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s;
}
</style>
<transition>
包裹需要?jiǎng)赢?huà)效果的元素肥荔,Vue會(huì)自動(dòng)添加一些類名
使用
<fade-animation>
<common-gallery :imgs="bannerImgs" v-show="showGallery" @close="handleGalleryClose"></common-gallery>
</fade-animation>
Vue項(xiàng)目的接口聯(lián)調(diào)
config/index.js
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
// target: 'http://localhost:8080',
target: 'http://localhost',
// pathRewrite: {
// 請(qǐng)求以/api開(kāi)頭的轉(zhuǎn)發(fā)到/static/mock
// '^/api': '/static/mock'
// }
}
},
...
}
'/api':
開(kāi)頭的請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到本地80端口绿渣,localhost
在上線階段應(yīng)更改為服務(wù)器地址或域名
Vue項(xiàng)目的測(cè)試
ifconfig
inet 192.168.1.103
該地址是本機(jī)在內(nèi)網(wǎng)的ip地址
假設(shè)我們的Vue項(xiàng)目運(yùn)行在8080端口,當(dāng)訪問(wèn)192.168.1.103:8080
時(shí)燕耿,會(huì)訪問(wèn)失敗中符,因?yàn)閣ebpack屏蔽了通過(guò)ip地址訪問(wèn),如果要解除這個(gè)限制誉帅,修改package.json
文件淀散,加上--host 0.0.0.0
"scripts": {
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},
這個(gè)時(shí)候,如果手機(jī)和電腦在同一局域網(wǎng)下蚜锨,就可以通過(guò)192.168.1.103:8080
訪問(wèn)頁(yè)面了
解決拖動(dòng)字母屏幕跟著滑動(dòng)
Alphabet.vue
@touchstart.prevent="handleTouchStart"
.prevent
可以解決這個(gè)問(wèn)題
解決瀏覽器不支持promise
npm install babel-polyfill --save
main.js
import 'babel-polyfill'
Vue項(xiàng)目的打包上線
- 運(yùn)行打包命令
npm run build
在項(xiàng)目根目錄會(huì)自動(dòng)生成dist文件夾档插,該文件夾的內(nèi)容是項(xiàng)目最終上線放到服務(wù)器的內(nèi)容
如果不想生成map文件,修改config/index.js
productionSourceMap: false,
重新打包即可
2 . 將dist文件夾下內(nèi)容上傳至服務(wù)器
將dist文件夾下的內(nèi)容放到服務(wù)器網(wǎng)站目錄根路徑下
-
api
是后端代碼 -
static
亚再、index.html
是打包后dist文件夾的內(nèi)容
這個(gè)時(shí)候郭膛,網(wǎng)站可以通過(guò)localhost
(本地配置了php或nginx)或服務(wù)器ip
訪問(wèn)
通過(guò)ip加訪問(wèn)路徑訪問(wèn)
我們希望通過(guò)ip地址/project
訪問(wèn)服務(wù)器,需要做如下修改
- 修改webpack配置
assetsPublicPath: '/project',
config/index.js
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
// assetsPublicPath: '/',
assetsPublicPath: '/project',
...
}
- 網(wǎng)站根目錄下新建
project
目錄 - 重新打包氛悬,把dist文件夾下的文件放到剛才新建的
project
目錄
異步組件
打包生成的app.js
包含所有頁(yè)面的業(yè)務(wù)邏輯代碼则剃,默認(rèn)情況下它會(huì)在瀏覽器第一次請(qǐng)求頁(yè)面時(shí)全部加載凄诞,顯然這樣不是很合理,我們希望訪問(wèn)某個(gè)頁(yè)面時(shí)忍级,只加載這個(gè)頁(yè)面的邏輯
router/index.js
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: () => import('@/pages/home/Home')
},
{
path: '/city',
name: 'City',
component: () => import('@/pages/city/City')
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('@/pages/detail/Detail')
}
],
// 當(dāng)進(jìn)行頁(yè)面切換時(shí)帆谍,x、y軸初始是0
scrollBehavior(to, form, savedPosition) {
return { x: 0, y: 0 }
}
})
- 組件中異步加載其他組件
components: {
HomeHeader: () => import('./components/Header'),
...
},
component: () => import('@/pages/home/Home')
解決了按需加載問(wèn)題
使用異步組件需要考慮的問(wèn)題:
- 異步組件可以減少首次加載時(shí)間轴咱,但在app.js文件本身較小時(shí)效果不明顯
- 異步組件缺點(diǎn)是會(huì)增加網(wǎng)絡(luò)請(qǐng)求
考慮以上因素汛蝙,在app.js文件較小時(shí)不使用異步組件
后續(xù)
Vue插件、自定義指令
vue-router路由守衛(wèi)等
vuex
Vue服務(wù)器端渲染
Vue插件
官方
第三方整理